diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000000..0bd2faed9d --- /dev/null +++ b/.cvsignore @@ -0,0 +1,6 @@ +BUILD +OWNERS +Makefile +README.google +runautoconf +config_auto.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000000..a0a208f0d9 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,8 @@ +Ray Smith (lead developer) +Phil Cheatle +Simon Crouch +Dan Johnson +Mark Seaman +Sheelagh Huddleston +Chris Newton +... and several others. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..20435711d7 --- /dev/null +++ b/COPYING @@ -0,0 +1,23 @@ +This package contains the Tesseract Open Source OCR Engine. +Orignally developed at Hewlett Packard Laboratories Bristol and +at Hewlett Packard Co, Greeley Colorado, all the code +in this distribution is now licensed under the Apache License: + +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. + + +Other Dependencies and Licenses: +================================ +The Aspirin/MIGRAINES system is no longer used. + +Tesseract can also make use of the libtiff library. (www.libtiff.org) +Without libtiff, Tesseract can only read uncompressed and G3 compressed +TIFF files. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000000..bcc09819ca --- /dev/null +++ b/ChangeLog @@ -0,0 +1,20 @@ +June 2006 - V1.0 of open source Tesseract checked-in. +Sep 7 2006 - V1.01. + Added mfcpch.cpp and getopt.cpp for VC++. + Fixed problem with greyscale images and no libtiff. + Stopped debug window from being used for the usage output. + Fixed load of inttemp for big-endian architectures. + Fixed some Mac compilation issues. +Oct 4 2006 - V1.02 + Removed dependency on Aspirin. + Fixed a few missing Apache license headers. + Removed $log. +Feb 2 2007 - V1.03 + Added mftraining and cntraining. + Added baseapi with adaptive thresholding for grey and color. + Fixed many memory leaks. + Fixed several bugs including lack of use of adaptive classifier. + Added ifdefs to eliminate graphics code and add embedded platform support. + Incorporated several patches, including 64-bit builds, Mac builds. + Minor accuracy improvements. + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000000..a4b34144dc --- /dev/null +++ b/INSTALL @@ -0,0 +1,229 @@ +Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000000..5ef53b5384 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,15 @@ +# TODO(luc) Add 'doc' to this list when ready +SUBDIRS = ccstruct ccutil classify cutil dict display image textord viewer wordrec ccmain training + +EXTRA_DIST = tessdata phototest.tif tesseract.dsp tesseract.dsw +#EXTRA_DIST = doc/html doc/@PACKAGE_NAME@_@PACKAGE_VERSION@.pdf doc/@PACKAGE_NAME@_@PACKAGE_VERSION@.ps.gz + +dist-hook: +# Need to remove CVS directories from directories +# added using EXTRA_DIST. $(distdir)/tessdata would in +# theory suffice. + rm -rf `find $(distdir) -name CVS` +# Also remove extra files not needed in a distribution + rm -rf `find $(distdir) -name configure.ac` + rm -rf `find $(distdir) -name acinclude.m4` + rm -rf `find $(distdir) -name aclocal.m4` diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000000..4017001d31 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,628 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = . +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(top_srcdir)/config/config.h.in \ + $(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \ + config/config.guess config/config.sub config/depcomp \ + config/install-sh config/missing config/mkinstalldirs +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno configure.status.lineno +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = config_auto.h +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + { test ! -d $(distdir) \ + || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr $(distdir); }; } +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ + +# TODO(luc) Add 'doc' to this list when ready +SUBDIRS = ccstruct ccutil classify cutil dict display image textord viewer wordrec ccmain training +EXTRA_DIST = tessdata phototest.tif tesseract.dsp tesseract.dsw +all: config_auto.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: + @: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --gnu '; \ + cd $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) + +config_auto.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) stamp-h1; \ + else :; fi + +stamp-h1: $(top_srcdir)/config/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config_auto.h +$(top_srcdir)/config/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_srcdir) && $(AUTOHEADER) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config_auto.h stamp-h1 +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(am__remove_distdir) + mkdir $(distdir) + $(mkdir_p) $(distdir)/config + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && cd $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}' +distuninstallcheck: + @cd $(distuninstallcheck_dir) \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile config_auto.h +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \ + check-am clean clean-generic clean-recursive ctags \ + ctags-recursive dist dist-all dist-bzip2 dist-gzip dist-hook \ + dist-shar dist-tarZ dist-zip distcheck distclean \ + distclean-generic distclean-hdr distclean-recursive \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am uninstall-info-am + +#EXTRA_DIST = doc/html doc/@PACKAGE_NAME@_@PACKAGE_VERSION@.pdf doc/@PACKAGE_NAME@_@PACKAGE_VERSION@.ps.gz + +dist-hook: +# Need to remove CVS directories from directories +# added using EXTRA_DIST. $(distdir)/tessdata would in +# theory suffice. + rm -rf `find $(distdir) -name CVS` +# Also remove extra files not needed in a distribution + rm -rf `find $(distdir) -name configure.ac` + rm -rf `find $(distdir) -name acinclude.m4` + rm -rf `find $(distdir) -name aclocal.m4` +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/NEWS b/NEWS new file mode 100644 index 0000000000..d2902ea6d9 --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +Stub file. To be populated at a later stage. diff --git a/README b/README new file mode 100644 index 0000000000..87eb84f442 --- /dev/null +++ b/README @@ -0,0 +1,85 @@ +Introduction +============ +This package contains the Tesseract Open Source OCR Engine. +Orignally developed at Hewlett Packard Laboratories Bristol and +at Hewlett Packard Co, Greeley Colorado, all the code +in this distribution is now licensed under the Apache License: + +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. + + +Other Dependencies and Licenses: +================================ +The Aspirin/MIGRAINES system is no longer required. + +Tesseract can also make use of the libtiff library. (www.libtiff.org) +Without libtiff, Tesseract can only read uncompressed and G3 compressed +TIFF files. + + +History: +======== +The engine was developed at Hewlett Packard Laboratories Bristol and +at Hewlett Packard Co, Greeley Colorado between 1985 and 1994, with some +more changes made in 1996 to port to Windows, and some C++izing in 1998. +A lot of the code was written in C, and then some more was written in C++. +Since then all the code has been converted to at least compile with a C++ +compiler. Currently it builds under Linux with gcc2.95 and under Windows +with VC++6. The C++ code makes heavy use of a list system using macros. +This predates stl, was portable before stl, and is more efficent than stl +lists, but has the big negative that if you do get a segmentation violation, +it is hard to debug. Another "feature" of the C/C++ split is that the C++ +data structures get converted to C data structures to call the low-level C +code. This is ugly, and the C++izing of the C code is a step towards +eliminating the conversion, but it has not happened yet. + + +Directory Structure (ordered by dependency): +============================================ +ccmain Top-level code. The main program resides in tesseractmain.cpp. +display An "editor" to view and operate on the internal structures. + (Requires a working viewer - batteries not included.) +wordrec The word-level recognizer. +textord The module that organizes(orders) text into lines and words. +classify The low-level character classifiers. +ccstruct Classes to hold information about a page as it is being processed. +viewer The client side of a client server viewing system. + Unfortunately, at this time, the server side is not available. +image Image class and processing functions. +dict Language model code. +cutil Code for file I/O, lists, heaps etc, from the old C code. +ccutil Somewhat newer code for lists, memory allocation etc from the + old C++ code. + + +About the Engine +================ +This code is a raw OCR engine. It has NO PAGE LAYOUT ANALYSIS, NO OUTPUT +FORMATTING, and NO UI. It can only process an image of a single column +and create text from it. It can detect fixed pitch vs proportional text. +Having said that, in 1995, this engine was in the top 3 in terms of character +accuracy, and it compiles and runs on both Linux and Windows. Another current +limitation is that it only recognizes English and its character set is only +US-ASCII. Training code IS included in the open source release however, and +will be included in a future release. + + +Using the Engine +================ +The usage of both Windows and Linux versions is the same. +The executable must reside in the same directory as the tessdata directory +The command line is: +tesseract batch +The image file requires an .tif extension for its type to be recognized +correctly. If a file exists with the .tif extension replaced by .uzn, then it +will be interpreted as a UNLV-style zone file. (See www.isri.unlv.edu for +details of the zone files.) + diff --git a/ReleaseNotes b/ReleaseNotes new file mode 100644 index 0000000000..f21f7afb08 --- /dev/null +++ b/ReleaseNotes @@ -0,0 +1,78 @@ +Tesseract release notes Feb 2, 2007 - V1.03. +Added mftraining and cntraining. Using an image with a box file, tesseract +generates .tr output files. cntraining runs on the .tr files to make +normproto that lives in tessdata. mftraining runs on the .tr files to +make inttemp and pffmtable in tessdata. These are the main data files +that tesseract uses to recognize characters. At present, the code to make +dictionary files is not yet available, nor are any sample box files or +rebuilt inttemp or documentation to create any of these. Recognition is +still limited to the ASCII set, but when this problem is fixed, documentation +will follow. + +Added a new API with adaptive thresholding for grey and color images. +See ccmain/baseapi.h/cpp for details. The main program has been converted +to use the API as an example. See main() in ccmain/tesseractmain.cpp for +details. The API is designed to make it easy to add subclasses with ability +to output the bounding boxes etc from the internal structures. The adaptive +thresholding improves accuracy (most of the time) on non-binary images. + +Many memory leaks have been fixed. There are no known leaks left from using +the API correctly. + +The adaptive classifier was not operating correctly. This bug, and several +others have been fixed, including poor chopping, an indefinite (if not quite +infinite) loop in the number parser, and a couple of crash bugs. Thanks to +all that have contributed bugs and bug fixes. + +It is now possible to build without any of the graphics support to save code +size using #define GRAPHICS_DISABLED. There is also a new EMBEDDED define +for use on operating systems with limited library support. + +64-bit and Mac OSX buildability is now included in the mainline source tree. +Thanks to all that have contributed patches and comments to help with that. +1.03 is also endian-independent, apart from the tiff i/o, so if you use +libtiff, the code should run on all platforms, even if you get/create new +data files of a different endinanness. + +Some of the bug fixes improve accuracy, and so do some of the changes to +DangAmbigs and user-words. + +Tesseract release notes, Oct 4 2006 - V1.02. +Removed dependency on aspirin. *All* code is now licensed under Apache2.0. + +Tesseract release notes, Sep 7 2006 - V1.01. + +Fixes for this release: +Added mfcpch.cpp and getopt.cpp for VC++. +Fixed problem with greyscale images and no libtiff. +Stopped debug window from being used for the usage output. +Fixed load of inttemp for big-endian architectures. +Fixed some Mac compilation issues. + +This version should read uncompressed 8 bit grey and 24 bit color tiffs +without having to have libtiff. It does a dumb threshold though, so don't +expect good results from poor contrast or images of natural scenes etc. + +If you just run tesseract with no command line args you should now get a +sensible usage message on linux, with or without X-windows. + +If you can get it to compile on a PPC Mac, it may now run correctly, +although not all the build issues are fixed yet. + +Building Tesseract: +Windows: +Unpack the tar.gz archive +Open tesseract.dsw in DevStudio (preferably version 6, higher versions will be more difficult) +Set Win32 - Release as the active configuration. +Build. +Copy tesseract.exe from bin.rel up one directory level. +Run tesseract phototest.tif phototest +This will create phototest.txt. + +Linux: +Unpack the tar.gz archive +./configure +make +Copy tesseract from ccmain up one directory level (or create a symbolic link) +Run tesseract phototest.tif phototest +This will create phototest.txt. diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000000..0ed906ad7d --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,10 @@ +# Master include for AC macros. This directory structure allows +# for more flexibility with respect to CVS modules. +# +# Author: Luc Vincent + +### m4_include(config/ac_compile_check_sizeof.m4)dnl +#m4_include(config/ac_create_stdint_h.m4)dnl +#m4_include(config/ax_create_stdint_h.m4)dnl +m4_include(config/ac_define_versionlevel.m4)dnl +m4_include(config/acinclude_custom.m4)dnl diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000000..bddd1ef0ee --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,920 @@ +# generated automatically by aclocal 1.9.6 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"]) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION so it can be traced. +# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], + [AM_AUTOMAKE_VERSION([1.9.6])]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 7 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ(2.52)dnl + ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 8 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH]) +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +#serial 3 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 8 + +# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. +AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 12 + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.58])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) +]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $1 | $1:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# Copyright (C) 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +AC_DEFUN([AM_MAINTAINER_MODE], +[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode is disabled by default + AC_ARG_ENABLE(maintainer-mode, +[ --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer], + USE_MAINTAINER_MODE=$enableval, + USE_MAINTAINER_MODE=no) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST(MAINT)dnl +] +) + +AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 3 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Copyright (C) 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 3 + +# AM_PROG_CC_C_O +# -------------- +# Like AC_PROG_CC_C_O, but changed for automake. +AC_DEFUN([AM_PROG_CC_C_O], +[AC_REQUIRE([AC_PROG_CC_C_O])dnl +AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +# FIXME: we rely on the cache variable name because +# there is no other way. +set dummy $CC +ac_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']` +if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" != yes"; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_MKDIR_P +# --------------- +# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise. +# +# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories +# created by `make install' are always world readable, even if the +# installer happens to have an overly restrictive umask (e.g. 077). +# This was a mistake. There are at least two reasons why we must not +# use `-m 0755': +# - it causes special bits like SGID to be ignored, +# - it may be too restrictive (some setups expect 775 directories). +# +# Do not use -m 0755 and let people choose whatever they expect by +# setting umask. +# +# We cannot accept any implementation of `mkdir' that recognizes `-p'. +# Some implementations (such as Solaris 8's) are not thread-safe: if a +# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c' +# concurrently, both version can detect that a/ is missing, but only +# one can create it and the other will error out. Consequently we +# restrict ourselves to GNU make (using the --version option ensures +# this.) +AC_DEFUN([AM_PROG_MKDIR_P], +[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi +AC_SUBST([mkdir_p])]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 3 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. +AM_MISSING_PROG([AMTAR], [tar]) +m4_if([$1], [v7], + [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([acinclude.m4]) diff --git a/ccmain/Makefile.am b/ccmain/Makefile.am new file mode 100644 index 0000000000..458f4dabff --- /dev/null +++ b/ccmain/Makefile.am @@ -0,0 +1,41 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer \ + -I$(top_srcdir)/ccops -I$(top_srcdir)/dict \ + -I$(top_srcdir)/classify -I$(top_srcdir)/display \ + -I$(top_srcdir)/wordrec -I$(top_srcdir)/cutil \ + -I$(top_srcdir)/textord + +EXTRA_DIST = \ + adaptions.h applybox.h baseapi.h blobcmp.h \ + callnet.h charcut.h \ + control.h docqual.h expandblob.h fixspace.h fixxht.h \ + imgscale.h matmatch.h output.h paircmp.h reject.h scaleimg.h \ + tessbox.h tessedit.h tesseractmain.h tessvars.h tfacep.h \ + tessembedded.h tfacepp.h tstruct.h werdit.h + +noinst_LIBRARIES = libtesseract_main.a +libtesseract_main_a_SOURCES = \ + tessedit.cpp adaptions.cpp applybox.cpp \ + baseapi.cpp blobcmp.cpp \ + callnet.cpp charcut.cpp charsample.cpp control.cpp \ + docqual.cpp expandblob.cpp fixspace.cpp fixxht.cpp \ + imgscale.cpp matmatch.cpp output.cpp paircmp.cpp \ + reject.cpp scaleimg.cpp tessbox.cpp tessvars.cpp \ + tfacepp.cpp tstruct.cpp werdit.cpp + +bin_PROGRAMS = tesseract +tesseract_SOURCES = tesseractmain.cpp +tesseract_LDADD = \ + libtesseract_main.a \ + ../display/libtesseract_display.a \ + ../textord/libtesseract_textord.a \ + ../wordrec/libtesseract_wordrec.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a \ + ../viewer/libtesseract_viewer.a \ + ../image/libtesseract_image.a \ + ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../ccutil/libtesseract_ccutil.a diff --git a/ccmain/Makefile.in b/ccmain/Makefile.in new file mode 100644 index 0000000000..ded1b7da84 --- /dev/null +++ b/ccmain/Makefile.in @@ -0,0 +1,636 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = tesseract$(EXEEXT) +subdir = ccmain +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_main_a_AR = $(AR) $(ARFLAGS) +libtesseract_main_a_LIBADD = +am_libtesseract_main_a_OBJECTS = tessedit.$(OBJEXT) \ + adaptions.$(OBJEXT) applybox.$(OBJEXT) baseapi.$(OBJEXT) \ + blobcmp.$(OBJEXT) callnet.$(OBJEXT) charcut.$(OBJEXT) \ + charsample.$(OBJEXT) control.$(OBJEXT) docqual.$(OBJEXT) \ + expandblob.$(OBJEXT) fixspace.$(OBJEXT) fixxht.$(OBJEXT) \ + imgscale.$(OBJEXT) matmatch.$(OBJEXT) output.$(OBJEXT) \ + paircmp.$(OBJEXT) reject.$(OBJEXT) scaleimg.$(OBJEXT) \ + tessbox.$(OBJEXT) tessvars.$(OBJEXT) tfacepp.$(OBJEXT) \ + tstruct.$(OBJEXT) werdit.$(OBJEXT) +libtesseract_main_a_OBJECTS = $(am_libtesseract_main_a_OBJECTS) +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_tesseract_OBJECTS = tesseractmain.$(OBJEXT) +tesseract_OBJECTS = $(am_tesseract_OBJECTS) +tesseract_DEPENDENCIES = libtesseract_main.a \ + ../display/libtesseract_display.a \ + ../textord/libtesseract_textord.a \ + ../wordrec/libtesseract_wordrec.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a ../viewer/libtesseract_viewer.a \ + ../image/libtesseract_image.a ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../ccutil/libtesseract_ccutil.a +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_main_a_SOURCES) $(tesseract_SOURCES) +DIST_SOURCES = $(libtesseract_main_a_SOURCES) $(tesseract_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer \ + -I$(top_srcdir)/ccops -I$(top_srcdir)/dict \ + -I$(top_srcdir)/classify -I$(top_srcdir)/display \ + -I$(top_srcdir)/wordrec -I$(top_srcdir)/cutil \ + -I$(top_srcdir)/textord + +EXTRA_DIST = \ + adaptions.h applybox.h baseapi.h blobcmp.h \ + callnet.h charcut.h \ + control.h docqual.h expandblob.h fixspace.h fixxht.h \ + imgscale.h matmatch.h output.h paircmp.h reject.h scaleimg.h \ + tessbox.h tessedit.h tesseractmain.h tessvars.h tfacep.h \ + tessembedded.h tfacepp.h tstruct.h werdit.h + +noinst_LIBRARIES = libtesseract_main.a +libtesseract_main_a_SOURCES = \ + tessedit.cpp adaptions.cpp applybox.cpp \ + baseapi.cpp blobcmp.cpp \ + callnet.cpp charcut.cpp charsample.cpp control.cpp \ + docqual.cpp expandblob.cpp fixspace.cpp fixxht.cpp \ + imgscale.cpp matmatch.cpp output.cpp paircmp.cpp \ + reject.cpp scaleimg.cpp tessbox.cpp tessvars.cpp \ + tfacepp.cpp tstruct.cpp werdit.cpp + +tesseract_SOURCES = tesseractmain.cpp +tesseract_LDADD = \ + libtesseract_main.a \ + ../display/libtesseract_display.a \ + ../textord/libtesseract_textord.a \ + ../wordrec/libtesseract_wordrec.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a \ + ../viewer/libtesseract_viewer.a \ + ../image/libtesseract_image.a \ + ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../ccutil/libtesseract_ccutil.a + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu ccmain/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu ccmain/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_main.a: $(libtesseract_main_a_OBJECTS) $(libtesseract_main_a_DEPENDENCIES) + -rm -f libtesseract_main.a + $(libtesseract_main_a_AR) libtesseract_main.a $(libtesseract_main_a_OBJECTS) $(libtesseract_main_a_LIBADD) + $(RANLIB) libtesseract_main.a +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +tesseract$(EXEEXT): $(tesseract_OBJECTS) $(tesseract_DEPENDENCIES) + @rm -f tesseract$(EXEEXT) + $(CXXLINK) $(tesseract_LDFLAGS) $(tesseract_OBJECTS) $(tesseract_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adaptions.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/applybox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/baseapi.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobcmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callnet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/charcut.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/charsample.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/docqual.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expandblob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fixspace.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fixxht.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imgscale.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/matmatch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/paircmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reject.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scaleimg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessbox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessedit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tesseractmain.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessvars.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tfacepp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tstruct.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/werdit.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ + clean-recursive ctags ctags-recursive distclean \ + distclean-compile distclean-generic distclean-recursive \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic maintainer-clean-recursive \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am uninstall-binPROGRAMS uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ccmain/adaptions.cpp b/ccmain/adaptions.cpp new file mode 100644 index 0000000000..913dd43b2b --- /dev/null +++ b/ccmain/adaptions.cpp @@ -0,0 +1,1078 @@ +/********************************************************************** + * File: adaptions.cpp (Formerly adaptions.c) + * Description: Functions used to adapt to blobs already confidently + * identified + * Author: Chris Newton + * Created: Thu Oct 7 10:17:28 BST 1993 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include +#include +#include "tessbox.h" +#include "tessvars.h" +#include "memry.h" +#include "mainblk.h" +#include "charcut.h" +#include "imgs.h" +#include "scaleimg.h" +#include "reject.h" +#include "control.h" +#include "adaptions.h" +#include "stopper.h" +#include "charsample.h" +#include "matmatch.h" +#include "secname.h" + +INT32 demo_word = 0; + +#define EXTERN + +EXTERN BOOL_VAR (tessedit_reject_ems, FALSE, "Reject all m's"); +EXTERN BOOL_VAR (tessedit_reject_suspect_ems, FALSE, "Reject suspect m's"); + +EXTERN double_VAR (tessedit_cluster_t1, 0.20, +"t1 threshold for clustering samples"); +EXTERN double_VAR (tessedit_cluster_t2, 0.40, +"t2 threshold for clustering samples"); +EXTERN double_VAR (tessedit_cluster_t3, 0.12, +"Extra threshold for clustering samples, only keep a new sample if best score greater than this value"); +EXTERN double_VAR (tessedit_cluster_accept_fraction, 0.80, +"Largest fraction of characters in cluster for it to be used for adaption"); +EXTERN INT_VAR (tessedit_cluster_min_size, 3, +"Smallest number of samples in a cluster for it to be used for adaption"); +EXTERN BOOL_VAR (tessedit_cluster_debug, FALSE, +"Generate and print debug information for adaption by clustering"); +EXTERN BOOL_VAR (tessedit_use_best_sample, FALSE, +"Use best sample from cluster when adapting"); +EXTERN BOOL_VAR (tessedit_test_cluster_input, FALSE, +"Set reject map to enable cluster input to be measured"); + +EXTERN BOOL_VAR (tessedit_matrix_match, TRUE, "Use matrix matcher"); +EXTERN BOOL_VAR (tessedit_mm_use_non_adaption_set, FALSE, +"Don't try to adapt to characters on this list"); +EXTERN STRING_VAR (tessedit_non_adaption_set, ",.;:'~@*", +"Characters to be avoided when adapting"); +EXTERN BOOL_VAR (tessedit_mm_adapt_using_prototypes, TRUE, +"Use prototypes when adapting"); +EXTERN BOOL_VAR (tessedit_mm_use_prototypes, TRUE, +"Use prototypes as clusters are built"); +EXTERN BOOL_VAR (tessedit_mm_use_rejmap, FALSE, +"Adapt to characters using reject map"); +EXTERN BOOL_VAR (tessedit_mm_all_rejects, FALSE, +"Adapt to all characters using, matrix matcher"); +EXTERN BOOL_VAR (tessedit_mm_only_match_same_char, FALSE, +"Only match samples against clusters for the same character"); +EXTERN BOOL_VAR (tessedit_process_rns, FALSE, "Handle m - rn ambigs"); + +EXTERN BOOL_VAR (tessedit_demo_adaption, FALSE, +"Display cut images and matrix match for demo purposes"); +EXTERN INT_VAR (tessedit_demo_word1, 62, +"Word number of first word to display"); +EXTERN INT_VAR (tessedit_demo_word2, 64, +"Word number of second word to display"); +EXTERN STRING_VAR (tessedit_demo_file, "academe", +"Name of document containing demo words"); + +BOOL8 word_adaptable( //should we adapt? + WERD_RES *word, + UINT16 mode) { + BOOL8 status = FALSE; + BITS16 flags(mode); + + enum MODES + { + ADAPTABLE_WERD, + ACCEPTABLE_WERD, + CHECK_DAWGS, + CHECK_SPACES, + CHECK_ONE_ELL_CONFLICT, + CHECK_AMBIG_WERD + }; + + /* + 0: NO adaption + */ + if (mode == 0) { + return FALSE; + } + + if (flags.bit (ADAPTABLE_WERD)) + status |= word->tess_would_adapt; + + if (flags.bit (ACCEPTABLE_WERD)) + status |= word->tess_accepted; + + if (!status) // If not set then + return FALSE; // ignore other checks + + if (flags.bit (CHECK_DAWGS) && + (word->best_choice->permuter () != SYSTEM_DAWG_PERM) && + (word->best_choice->permuter () != FREQ_DAWG_PERM) && + (word->best_choice->permuter () != USER_DAWG_PERM) && + (word->best_choice->permuter () != NUMBER_PERM)) + return FALSE; + + if (flags.bit (CHECK_ONE_ELL_CONFLICT) && one_ell_conflict (word, FALSE)) + return FALSE; + + if (flags.bit (CHECK_SPACES) && + (strchr (word->best_choice->string ().string (), ' ') != NULL)) + return FALSE; + +// if (flags.bit (CHECK_AMBIG_WERD) && test_ambig_word (word)) + if (flags.bit (CHECK_AMBIG_WERD) && + !NoDangerousAmbig(word->best_choice->string().string(), NULL)) + return FALSE; + + return status; + +} + + +void collect_ems_for_adaption(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 i; + CHAR_SAMPLE *sample; + PIXROW_LIST *pixrow_list; + PIXROW_IT pixrow_it; + IMAGELINE *imlines; // lines of the image + BOX pix_box; // box of imlines + // extent + WERD copy_outword; // copy to denorm + PBLOB_IT copy_blob_it; + OUTLINE_IT copy_outline_it; + INT32 resolution = page_image.get_res (); + + if (tessedit_reject_ems || tessedit_reject_suspect_ems) + return; // Do nothing + + if (word->word->bounding_box ().height () > resolution / 3) + return; + + if (tessedit_demo_adaption) + // Make sure not set + tessedit_display_mm.set_value (FALSE); + + if (word_adaptable (word, tessedit_em_adaption_mode) + && word->reject_map.reject_count () == 0 + && (strchr (word->best_choice->string ().string (), 'm') != NULL + || (tessedit_process_rns + && strstr (word->best_choice->string ().string (), + "rn") != NULL))) { + if (tessedit_process_rns + && strstr (word->best_choice->string ().string (), "rn") != NULL) { + copy_outword = *(word->outword); + copy_blob_it.set_to_list (copy_outword.blob_list ()); + i = 0; + while (word->best_choice->string ()[i] != '\0') { + if (word->best_choice->string ()[i] == 'r' + && word->best_choice->string ()[i + 1] == 'n') { + copy_outline_it.set_to_list (copy_blob_it.data ()-> + out_list ()); + copy_outline_it.add_list_after (copy_blob_it. + data_relative (1)-> + out_list ()); + copy_blob_it.forward (); + delete (copy_blob_it.extract ()); + i++; + } + copy_blob_it.forward (); + i++; + } + } + else + copy_outword = *(word->outword); + + copy_outword.baseline_denormalise (&word->denorm); + char_clip_word(©_outword, page_image, pixrow_list, imlines, pix_box); + pixrow_it.set_to_list (pixrow_list); + pixrow_it.move_to_first (); + + blob_it.move_to_first (); + for (i = 0; + word->best_choice->string ()[i] != '\0'; + i++, pixrow_it.forward (), blob_it.forward ()) { + + if (word->best_choice->string ()[i] == 'm' + || (word->best_choice->string ()[i] == 'r' + && word->best_choice->string ()[i + 1] == 'n')) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample %c for adaption found in %s, index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), i); + #endif + if (tessedit_matrix_match) { + sample = clip_sample (pixrow_it.data (), + imlines, + pix_box, + copy_outword.flag (W_INVERSE), + word->best_choice->string ()[i]); + + if (sample == NULL) { //Clip failed + #ifndef SECURE_NAMES + tprintf ("Unable to clip sample from %s, index %d\n", + word->best_choice->string ().string (), i); + #endif + if (word->best_choice->string ()[i] == 'r') + i++; + + continue; + } + } + else + sample = new CHAR_SAMPLE (blob_it.data (), + &word->denorm, + word->best_choice->string ()[i]); + + cluster_sample(sample, char_clusters, chars_waiting); + + if (word->best_choice->string ()[i] == 'r') + i++; // Skip next character + } + } + delete[]imlines; // Free array of imlines + delete pixrow_list; + } +} + + +void collect_characters_for_adaption(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 i; + CHAR_SAMPLE *sample; + PIXROW_LIST *pixrow_list; + PIXROW_IT pixrow_it; + IMAGELINE *imlines; // lines of the image + BOX pix_box; // box of imlines + // extent + WERD copy_outword; // copy to denorm + INT32 resolution = page_image.get_res (); + + if (word->word->bounding_box ().height () > resolution / 3) + return; + + if (tessedit_demo_adaption) + // Make sure not set + tessedit_display_mm.set_value (FALSE); + + if ((word_adaptable (word, tessedit_cluster_adaption_mode) + && word->reject_map.reject_count () == 0) || tessedit_mm_use_rejmap) { + if (tessedit_test_cluster_input && !tessedit_mm_use_rejmap) + return; // Reject map set to acceptable + /* Collect information about good matches */ + copy_outword = *(word->outword); + copy_outword.baseline_denormalise (&word->denorm); + char_clip_word(©_outword, page_image, pixrow_list, imlines, pix_box); + pixrow_it.set_to_list (pixrow_list); + pixrow_it.move_to_first (); + + blob_it.move_to_first (); + for (i = 0; + word->best_choice->string ()[i] != '\0'; + i++, pixrow_it.forward (), blob_it.forward ()) { + + if (!(tessedit_mm_use_non_adaption_set + && STRING (tessedit_non_adaption_set).contains (word-> + best_choice-> + string ()[i])) + || (tessedit_mm_use_rejmap && word->reject_map[i].accepted ())) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample %c for adaption found in %s, index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), i); + #endif + sample = clip_sample (pixrow_it.data (), + imlines, + pix_box, + copy_outword.flag (W_INVERSE), + word->best_choice->string ()[i]); + + if (sample == NULL) { //Clip failed + #ifndef SECURE_NAMES + tprintf ("Unable to clip sample from %s, index %d\n", + word->best_choice->string ().string (), i); + #endif + continue; + } + cluster_sample(sample, char_clusters, chars_waiting); + } + } + delete[]imlines; // Free array of imlines + delete pixrow_list; + } + else if (tessedit_test_cluster_input && !tessedit_mm_use_rejmap) + // Set word to all rejects + word->reject_map.rej_word_tess_failure (); + +} + + +void cluster_sample(CHAR_SAMPLE *sample, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + CHAR_SAMPLES *best_cluster = NULL; + CHAR_SAMPLES_IT c_it = char_clusters; + CHAR_SAMPLE_IT cw_it = chars_waiting; + float score; + float best_score = MAX_INT32; + + if (c_it.empty ()) + c_it.add_to_end (new CHAR_SAMPLES (sample)); + else { + for (c_it.mark_cycle_pt (); !c_it.cycled_list (); c_it.forward ()) { + score = c_it.data ()->match_score (sample); + if (score < best_score) { + best_score = score; + best_cluster = c_it.data (); + } + } + + if (tessedit_cluster_debug) + tprintf ("Sample's best score %f\n", best_score); + + if (best_score < tessedit_cluster_t1) { + if (best_score > tessedit_cluster_t3 || tessedit_mm_use_prototypes) { + best_cluster->add_sample (sample); + check_wait_list(chars_waiting, sample, best_cluster); + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample added to an existing cluster\n"); + #endif + } + else { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf + ("Sample dropped, good match to an existing cluster\n"); + #endif + } + } + else if (best_score > tessedit_cluster_t2) { + c_it.add_to_end (new CHAR_SAMPLES (sample)); + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("New cluster created for this sample\n"); + #endif + } + else { + cw_it.add_to_end (sample); + if (tessedit_cluster_debug) + tprintf ("Sample added to the wait list\n"); + } + } +} + + +void check_wait_list(CHAR_SAMPLE_LIST *chars_waiting, + CHAR_SAMPLE *sample, + CHAR_SAMPLES *best_cluster) { + CHAR_SAMPLE *wait_sample; + CHAR_SAMPLE *test_sample = sample; + CHAR_SAMPLE_IT cw_it = chars_waiting; + CHAR_SAMPLE_LIST add_list; //Samples added to best cluster + CHAR_SAMPLE_IT add_it = &add_list; + float score; + + add_list.clear (); + + if (!cw_it.empty ()) { + do { + if (!add_list.empty ()) { + add_it.forward (); + test_sample = add_it.extract (); + best_cluster->add_sample (test_sample); + } + + for (cw_it.mark_cycle_pt (); + !cw_it.cycled_list (); cw_it.forward ()) { + wait_sample = cw_it.data (); + if (tessedit_mm_use_prototypes) + score = best_cluster->match_score (wait_sample); + else + score = sample->match_sample (wait_sample, FALSE); + if (score < tessedit_cluster_t1) { + if (score > tessedit_cluster_t3 + || tessedit_mm_use_prototypes) { + add_it.add_after_stay_put (cw_it.extract ()); + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf + ("Wait sample added to an existing cluster\n"); + #endif + } + else { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf + ("Wait sample dropped, good match to an existing cluster\n"); + #endif + } + } + } + } + while (!add_list.empty ()); + } +} + + +void complete_clustering(CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + CHAR_SAMPLES *best_cluster; + CHAR_SAMPLES_IT c_it = char_clusters; + CHAR_SAMPLE_IT cw_it = chars_waiting; + CHAR_SAMPLE *sample; + INT32 total_sample_count = 0; + + while (!cw_it.empty ()) { + cw_it.move_to_first (); + sample = cw_it.extract (); + best_cluster = new CHAR_SAMPLES (sample); + c_it.add_to_end (best_cluster); + check_wait_list(chars_waiting, sample, best_cluster); + } + + for (c_it.mark_cycle_pt (); !c_it.cycled_list (); c_it.forward ()) { + c_it.data ()->assign_to_char (); + if (tessedit_use_best_sample) + c_it.data ()->find_best_sample (); + else if (tessedit_mm_adapt_using_prototypes) + c_it.data ()->build_prototype (); + + if (tessedit_cluster_debug) + total_sample_count += c_it.data ()->n_samples (); + } + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Clustering completed, %d samples in all\n", total_sample_count); + #endif + +#ifndef GRAPHICS_DISABLED + if (tessedit_demo_adaption) + display_cluster_prototypes(char_clusters); +#endif + +} + + +void adapt_to_good_ems(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 i; + CHAR_SAMPLE *sample; + CHAR_SAMPLES_IT c_it = char_clusters; + CHAR_SAMPLE_IT cw_it = chars_waiting; + float score; + float best_score; + char best_char; + CHAR_SAMPLES *best_cluster; + PIXROW_LIST *pixrow_list; + PIXROW_IT pixrow_it; + IMAGELINE *imlines; // lines of the image + BOX pix_box; // box of imlines + // extent + WERD copy_outword; // copy to denorm + BOX b_box; + PBLOB_IT copy_blob_it; + OUTLINE_IT copy_outline_it; + PIXROW *pixrow = NULL; + + static INT32 word_number = 0; + +#ifndef GRAPHICS_DISABLED + WINDOW demo_win = NULL; +#endif + + INT32 resolution = page_image.get_res (); + + if (word->word->bounding_box ().height () > resolution / 3) + return; + + word_number++; + + if (strchr (word->best_choice->string ().string (), 'm') == NULL + && (tessedit_process_rns + && strstr (word->best_choice->string ().string (), "rn") == NULL)) + return; + + if (tessedit_reject_ems) + reject_all_ems(word); + else if (tessedit_reject_suspect_ems) + reject_suspect_ems(word); + else { + if (char_clusters->length () == 0) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("No clusters to use for em adaption\n"); + #endif + return; + } + + if (!cw_it.empty ()) { + complete_clustering(char_clusters, chars_waiting); + print_em_stats(char_clusters, chars_waiting); + } + + if ((!word_adaptable (word, tessedit_em_adaption_mode) || + word->reject_map.reject_count () != 0) + && (strchr (word->best_choice->string ().string (), 'm') != NULL + || (tessedit_process_rns + && strstr (word->best_choice->string ().string (), + "rn") != NULL))) { + if (tessedit_process_rns + && strstr (word->best_choice->string ().string (), + "rn") != NULL) { + copy_outword = *(word->outword); + copy_blob_it.set_to_list (copy_outword.blob_list ()); + i = 0; + while (word->best_choice->string ()[i] != '\0') { + if (word->best_choice->string ()[i] == 'r' + && word->best_choice->string ()[i + 1] == 'n') { + copy_outline_it.set_to_list (copy_blob_it.data ()-> + out_list ()); + copy_outline_it.add_list_after (copy_blob_it. + data_relative (1)-> + out_list ()); + copy_blob_it.forward (); + delete (copy_blob_it.extract ()); + i++; + } + copy_blob_it.forward (); + i++; + } + } + else + copy_outword = *(word->outword); + + copy_outword.baseline_denormalise (&word->denorm); + copy_blob_it.set_to_list (copy_outword.blob_list ()); + char_clip_word(©_outword, page_image, pixrow_list, imlines, pix_box); + pixrow_it.set_to_list (pixrow_list); + pixrow_it.move_to_first (); + + // For debugging only + b_box = copy_outword.bounding_box (); + pixrow = pixrow_it.data (); + + blob_it.move_to_first (); + copy_blob_it.move_to_first (); + for (i = 0; + word->best_choice->string ()[i] != '\0'; + i++, pixrow_it.forward (), blob_it.forward (), + copy_blob_it.forward ()) { + if ((word->best_choice->string ()[i] == 'm' + || (word->best_choice->string ()[i] == 'r' + && word->best_choice->string ()[i + 1] == 'n')) + && !word->reject_map[i].perm_rejected ()) { + if (tessedit_cluster_debug) + tprintf ("Sample %c to check found in %s, index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), i); + + if (tessedit_demo_adaption) + tprintf + ("Sample %c to check found in %s (%d), index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), word_number, + i); + + if (tessedit_matrix_match) { + BOX copy_box = copy_blob_it.data ()->bounding_box (); + + sample = clip_sample (pixrow_it.data (), + imlines, + pix_box, + copy_outword.flag (W_INVERSE), + word->best_choice->string ()[i]); + + //Clip failed + if (sample == NULL) { + tprintf + ("Unable to clip sample from %s, index %d\n", + word->best_choice->string ().string (), i); + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (no sample)\n"); + #endif + word->reject_map[i].setrej_mm_reject (); + if (word->best_choice->string ()[i] == 'r') { + word->reject_map[i + 1].setrej_mm_reject (); + i++; + } + continue; + } + } + else + sample = new CHAR_SAMPLE (blob_it.data (), + &word->denorm, + word->best_choice-> + string ()[i]); + + best_score = MAX_INT32; + best_char = '\0'; + best_cluster = NULL; + + for (c_it.mark_cycle_pt (); + !c_it.cycled_list (); c_it.forward ()) { + if (c_it.data ()->character () != '\0') { + score = c_it.data ()->match_score (sample); + if (score < best_score) { + best_cluster = c_it.data (); + best_score = score; + best_char = c_it.data ()->character (); + } + } + } + + if (best_score > tessedit_cluster_t1) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (score %f)\n", best_score); + if (tessedit_demo_adaption) + tprintf ("Sample rejected (score %f)\n", best_score); + #endif + word->reject_map[i].setrej_mm_reject (); + if (word->best_choice->string ()[i] == 'r') + word->reject_map[i + 1].setrej_mm_reject (); + } + else { + if (word->best_choice->string ()[i] == best_char) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample accepted (score %f)\n", + best_score); + if (tessedit_demo_adaption) + tprintf ("Sample accepted (score %f)\n", + best_score); + #endif + word->reject_map[i].setrej_mm_accept (); + if (word->best_choice->string ()[i] == 'r') + word->reject_map[i + 1].setrej_mm_accept (); + } + else { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (char %c, score %f)\n", + best_char, best_score); + if (tessedit_demo_adaption) + tprintf ("Sample rejected (char %c, score %f)\n", + best_char, best_score); + #endif + word->reject_map[i].setrej_mm_reject (); + if (word->best_choice->string ()[i] == 'r') + word->reject_map[i + 1].setrej_mm_reject (); + } + } + + if (tessedit_demo_adaption) { + if (strcmp (imagebasename.string (), + tessedit_demo_file.string ()) != 0 + || word_number == tessedit_demo_word1 + || word_number == tessedit_demo_word2) { +#ifndef GRAPHICS_DISABLED + demo_win = + display_clip_image(©_outword, + page_image, + pixrow_list, + pix_box); +#endif + demo_word = word_number; + best_cluster->match_score (sample); + demo_word = 0; + } + } + if (word->best_choice->string ()[i] == 'r') + i++; // Skip next character + } + } + delete[]imlines; // Free array of imlines + delete pixrow_list; + } + } +} + + +void adapt_to_good_samples(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 i; + CHAR_SAMPLE *sample; + CHAR_SAMPLES_IT c_it = char_clusters; + CHAR_SAMPLE_IT cw_it = chars_waiting; + float score; + float best_score; + char best_char; + CHAR_SAMPLES *best_cluster; + PIXROW_LIST *pixrow_list; + PIXROW_IT pixrow_it; + IMAGELINE *imlines; // lines of the image + BOX pix_box; // box of imlines + // extent + WERD copy_outword; // copy to denorm + BOX b_box; + PBLOB_IT copy_blob_it; + PIXROW *pixrow = NULL; + + static INT32 word_number = 0; + +#ifndef GRAPHICS_DISABLED + WINDOW demo_win = NULL; +#endif + + INT32 resolution = page_image.get_res (); + + word_number++; + + if (tessedit_test_cluster_input) + return; + + if (word->word->bounding_box ().height () > resolution / 3) + return; + + if (char_clusters->length () == 0) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("No clusters to use for adaption\n"); + #endif + return; + } + + if (!cw_it.empty ()) { + complete_clustering(char_clusters, chars_waiting); + print_em_stats(char_clusters, chars_waiting); + } + + if ((!word_adaptable (word, tessedit_cluster_adaption_mode) + && word->reject_map.reject_count () != 0) || tessedit_mm_use_rejmap) { + if (tessedit_cluster_debug) { + tprintf ("\nChecking: \"%s\" MAP ", + word->best_choice->string ().string ()); + word->reject_map.print (debug_fp); + tprintf ("\n"); + } + + copy_outword = *(word->outword); + copy_outword.baseline_denormalise (&word->denorm); + copy_blob_it.set_to_list (copy_outword.blob_list ()); + char_clip_word(©_outword, page_image, pixrow_list, imlines, pix_box); + pixrow_it.set_to_list (pixrow_list); + pixrow_it.move_to_first (); + + // For debugging only + b_box = copy_outword.bounding_box (); + pixrow = pixrow_it.data (); + + blob_it.move_to_first (); + copy_blob_it.move_to_first (); + for (i = 0; + word->best_choice->string ()[i] != '\0'; + i++, pixrow_it.forward (), blob_it.forward (), + copy_blob_it.forward ()) { + if (word->reject_map[i].recoverable () + || (tessedit_mm_all_rejects && word->reject_map[i].rejected ())) { + BOX copy_box = copy_blob_it.data ()->bounding_box (); + + if (tessedit_cluster_debug) + tprintf ("Sample %c to check found in %s, index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), i); + + if (tessedit_demo_adaption) + tprintf ("Sample %c to check found in %s (%d), index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), + word_number, i); + + sample = clip_sample (pixrow_it.data (), + imlines, + pix_box, + copy_outword.flag (W_INVERSE), + word->best_choice->string ()[i]); + + if (sample == NULL) { //Clip failed + tprintf ("Unable to clip sample from %s, index %d\n", + word->best_choice->string ().string (), i); + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (no sample)\n"); + #endif + word->reject_map[i].setrej_mm_reject (); + + continue; + } + + best_score = MAX_INT32; + best_char = '\0'; + best_cluster = NULL; + + for (c_it.mark_cycle_pt (); + !c_it.cycled_list (); c_it.forward ()) { + if (c_it.data ()->character () != '\0') { + score = c_it.data ()->match_score (sample); + if (score < best_score) { + best_cluster = c_it.data (); + best_score = score; + best_char = c_it.data ()->character (); + } + } + } + + if (best_score > tessedit_cluster_t1) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (score %f)\n", best_score); + if (tessedit_demo_adaption) + tprintf ("Sample rejected (score %f)\n", best_score); + #endif + word->reject_map[i].setrej_mm_reject (); + } + else { + if (word->best_choice->string ()[i] == best_char) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample accepted (score %f)\n", best_score); + if (tessedit_demo_adaption) + tprintf ("Sample accepted (score %f)\n", best_score); + #endif + if (tessedit_test_adaption) + word->reject_map[i].setrej_minimal_rej_accept (); + else + word->reject_map[i].setrej_mm_accept (); + } + else { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (char %c, score %f)\n", + best_char, best_score); + if (tessedit_demo_adaption) + tprintf ("Sample rejected (char %c, score %f)\n", + best_char, best_score); + #endif + word->reject_map[i].setrej_mm_reject (); + } + } + + if (tessedit_demo_adaption) { + if (strcmp (imagebasename.string (), + tessedit_demo_file.string ()) != 0 + || word_number == tessedit_demo_word1 + || word_number == tessedit_demo_word2) { +#ifndef GRAPHICS_DISABLED + demo_win = + display_clip_image(©_outword, + page_image, + pixrow_list, + pix_box); +#endif + demo_word = word_number; + best_cluster->match_score (sample); + demo_word = 0; + } + } + } + } + delete[]imlines; // Free array of imlines + delete pixrow_list; + + if (tessedit_cluster_debug) { + tprintf ("\nFinal: \"%s\" MAP ", + word->best_choice->string ().string ()); + word->reject_map.print (debug_fp); + tprintf ("\n"); + } + } +} + + +void print_em_stats(CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + CHAR_SAMPLES_IT c_it = char_clusters; + + if (!tessedit_cluster_debug) + return; + #ifndef SECURE_NAMES + tprintf ("There are %d clusters and %d samples waiting\n", + char_clusters->length (), chars_waiting->length ()); + + for (c_it.mark_cycle_pt (); !c_it.cycled_list (); c_it.forward ()) + c_it.data ()->print (debug_fp); + #endif + tprintf ("\n"); +} + + +CHAR_SAMPLE *clip_sample( //lines of the image + PIXROW *pixrow, + IMAGELINE *imlines, + BOX pix_box, //box of imlines extent + BOOL8 white_on_black, + char c) { + BOX b_box = pixrow->bounding_box (); + float baseline_pos = 0; + INT32 resolution = page_image.get_res (); + + if (!b_box.null_box ()) { + ASSERT_HOST (b_box.width () < page_image.get_xsize () && + b_box.height () < page_image.get_ysize ()); + + if (b_box.width () > resolution || b_box.height () > resolution) { + tprintf ("clip sample: sample too big (%d x %d)\n", + b_box.width (), b_box.height ()); + + return NULL; + } + + IMAGE *image = new (IMAGE); + if (image->create (b_box.width (), b_box.height (), 1) == -1) { + tprintf ("clip sample: create image failed (%d x %d)\n", + b_box.width (), b_box.height ()); + + delete image; + return NULL; + } + + if (!white_on_black) + invert_image(image); // Set background to white + pixrow->char_clip_image (imlines, pix_box, NULL, *image, baseline_pos); + if (white_on_black) + invert_image(image); //invert white on black for scaling &NN + return new CHAR_SAMPLE (image, c); + } + else + return NULL; +} + + +#ifndef GRAPHICS_DISABLED +void display_cluster_prototypes(CHAR_SAMPLES_LIST *char_clusters) { + INT16 proto_number = 0; + CHAR_SAMPLES_IT c_it = char_clusters; + char title[WINDOWNAMESIZE]; + + for (c_it.mark_cycle_pt (); !c_it.cycled_list (); c_it.forward ()) { + proto_number++; + + #ifndef SECURE_NAMES + tprintf ("Displaying proto number %d\n", proto_number); + #endif + + if (c_it.data ()->prototype () != NULL) { + sprintf (title, "Proto - %d", proto_number); + display_image (c_it.data ()->prototype ()->make_image (), + title, (proto_number - 1) * 400, 0, FALSE); + } + } +} +#endif + +// ********************************************************************* +// Simplistic routines to test the effect of rejecting ems and fullstops +// ********************************************************************* + +void reject_all_ems(WERD_RES *word) { + INT16 i; + + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (word->best_choice->string ()[i] == 'm') + // reject all ems + word->reject_map[i].setrej_mm_reject (); + } +} + + +void reject_all_fullstops(WERD_RES *word) { + INT16 i; + + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (word->best_choice->string ()[i] == '.') + // reject all fullstops + word->reject_map[i].setrej_mm_reject (); + } +} + + +void reject_suspect_ems(WERD_RES *word) { + INT16 i; + + if (!word_adaptable (word, tessedit_cluster_adaption_mode)) + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (word->best_choice->string ()[i] == 'm' && suspect_em (word, i)) + // reject all ems + word->reject_map[i].setrej_mm_reject (); + } +} + + +void reject_suspect_fullstops(WERD_RES *word) { + INT16 i; + + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (word->best_choice->string ()[i] == '.' + && suspect_fullstop (word, i)) + // reject all commas + word->reject_map[i].setrej_mm_reject (); + } +} + + +BOOL8 suspect_em(WERD_RES *word, INT16 index) { + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 j; + + for (j = 0; j < index; j++) + blob_it.forward (); + + return (blob_it.data ()->out_list ()->length () != 1); +} + + +BOOL8 suspect_fullstop(WERD_RES *word, INT16 i) { + float aspect_ratio; + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 j; + BOX box; + INT16 width; + INT16 height; + + for (j = 0; j < i; j++) + blob_it.forward (); + + box = blob_it.data ()->bounding_box (); + + width = box.width (); + height = box.height (); + + aspect_ratio = ((width > height) ? ((float) width) / height : + ((float) height) / width); + + return (aspect_ratio > tessed_fullstop_aspect_ratio); +} diff --git a/ccmain/adaptions.h b/ccmain/adaptions.h new file mode 100644 index 0000000000..5719b9060c --- /dev/null +++ b/ccmain/adaptions.h @@ -0,0 +1,109 @@ +/********************************************************************** + * File: adaptions.h (Formerly adaptions.h) + * Description: Functions used to adapt to blobs already confidently + * identified + * Author: Chris Newton + * Created: Thu Oct 7 10:17:28 BST 1993 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef ADAPTIONS_H +#define ADAPTIONS_H + +#include "charsample.h" +#include "charcut.h" +#include "notdll.h" + +extern BOOL_VAR_H (tessedit_reject_ems, FALSE, "Reject all m's"); +extern BOOL_VAR_H (tessedit_reject_suspect_ems, FALSE, "Reject suspect m's"); +extern double_VAR_H (tessedit_cluster_t1, 0.20, +"t1 threshold for clustering samples"); +extern double_VAR_H (tessedit_cluster_t2, 0.40, +"t2 threshold for clustering samples"); +extern double_VAR_H (tessedit_cluster_t3, 0.12, +"Extra threshold for clustering samples, only keep a new sample if best score greater than this value"); +extern double_VAR_H (tessedit_cluster_accept_fraction, 0.80, +"Largest fraction of characters in cluster for it to be used for adaption"); +extern INT_VAR_H (tessedit_cluster_min_size, 3, +"Smallest number of samples in a cluster for it to be used for adaption"); +extern BOOL_VAR_H (tessedit_cluster_debug, FALSE, +"Generate and print debug information for adaption by clustering"); +extern BOOL_VAR_H (tessedit_use_best_sample, FALSE, +"Use best sample from cluster when adapting"); +extern BOOL_VAR_H (tessedit_test_cluster_input, FALSE, +"Set reject map to enable cluster input to be measured"); +extern BOOL_VAR_H (tessedit_matrix_match, TRUE, "Use matrix matcher"); +extern BOOL_VAR_H (tessedit_old_matrix_match, FALSE, "Use matrix matcher"); +extern BOOL_VAR_H (tessedit_mm_use_non_adaption_set, FALSE, +"Don't try to adapt to characters on this list"); +extern STRING_VAR_H (tessedit_non_adaption_set, ",.;:'~@*", +"Characters to be avoided when adapting"); +extern BOOL_VAR_H (tessedit_mm_adapt_using_prototypes, TRUE, +"Use prototypes when adapting"); +extern BOOL_VAR_H (tessedit_mm_use_prototypes, TRUE, +"Use prototypes as clusters are built"); +extern BOOL_VAR_H (tessedit_mm_use_rejmap, FALSE, +"Adapt to characters using reject map"); +extern BOOL_VAR_H (tessedit_mm_all_rejects, FALSE, +"Adapt to all characters using, matrix matcher"); +extern BOOL_VAR_H (tessedit_mm_only_match_same_char, FALSE, +"Only match samples against clusters for the same character"); +extern BOOL_VAR_H (tessedit_process_rns, FALSE, "Handle m - rn ambigs"); +extern BOOL_VAR_H (tessedit_demo_adaption, FALSE, +"Display cut images and matrix match for demo purposes"); +extern INT_VAR_H (tessedit_demo_word1, 62, +"Word number of first word to display"); +extern INT_VAR_H (tessedit_demo_word2, 64, +"Word number of second word to display"); +extern STRING_VAR_H (tessedit_demo_file, "academe", +"Name of document containing demo words"); +BOOL8 word_adaptable( //should we adapt? + WERD_RES *word, + UINT16 mode); +void collect_ems_for_adaption(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void collect_characters_for_adaption(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void cluster_sample(CHAR_SAMPLE *sample, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void check_wait_list(CHAR_SAMPLE_LIST *chars_waiting, + CHAR_SAMPLE *sample, + CHAR_SAMPLES *best_cluster); +void complete_clustering(CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void adapt_to_good_ems(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void adapt_to_good_samples(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void print_em_stats(CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); + //lines of the image +CHAR_SAMPLE *clip_sample(PIXROW *pixrow, + IMAGELINE *imlines, + BOX pix_box, //box of imlines extent + BOOL8 white_on_black, + char c); +void display_cluster_prototypes(CHAR_SAMPLES_LIST *char_clusters); +void reject_all_ems(WERD_RES *word); +void reject_all_fullstops(WERD_RES *word); +void reject_suspect_ems(WERD_RES *word); +void reject_suspect_fullstops(WERD_RES *word); +BOOL8 suspect_em(WERD_RES *word, INT16 index); +BOOL8 suspect_fullstop(WERD_RES *word, INT16 i); +#endif diff --git a/ccmain/applybox.cpp b/ccmain/applybox.cpp new file mode 100644 index 0000000000..41b4822596 --- /dev/null +++ b/ccmain/applybox.cpp @@ -0,0 +1,859 @@ +/********************************************************************** + * File: applybox.cpp (Formerly applybox.c) + * Description: Re segment rows according to box file data + * Author: Phil Cheatle + * Created: Wed Nov 24 09:11:23 GMT 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +/* +define SECURE_NAMES for code versions which go to UNLV to stop tessedit +including all the newdiff stuff (which contains lots of text indicating +what measures we are interested in. +*/ +/* #define SECURE_NAMES done in secnames.h when necessary*/ + +#include "mfcpch.h" +#include "applybox.h" +#include +#include +#ifdef __UNIX__ +#include +#include +#endif +#include "mainblk.h" +#include "genblob.h" +#include "fixxht.h" +#include "control.h" +#include "tessbox.h" +#include "globals.h" +#include "secname.h" + +#define SECURE_NAMES +#ifndef SECURE_NAMES +#include "wordstats.h" +#endif + +#define EXTERN +EXTERN BOOL_VAR (applybox_rebalance, TRUE, "Drop dead"); +EXTERN INT_VAR (applybox_debug, 0, "Debug level"); +EXTERN STRING_VAR (applybox_test_exclusions, "|", +"Chars ignored for testing"); +EXTERN double_VAR (applybox_error_band, 0.15, "Err band as fract of xht"); + +/************************************************************************* + * The code re-assigns outlines to form words each with ONE labelled blob. + * Noise is left in UNLABELLED words. The chars on the page are checked crudely + * for sensible position relative to baseline and xht. Failed boxes are + * compensated for by duplicating other believable instances of the character. + * + * The box file is assumed to contain box definitions, one per line, of the + * following format: + * ... arbitrary trailing fields unused + * + * The approach taken is to search the WHOLE page for stuff overlapping each box. + * - This is not too inefficient and is SAFE. + * - We can detect overlapping blobs as we will be attempting to put a blob + * from a LABELLED word into the current word. + * - When all the boxes have been processed we can detect any stuff which is + * being ignored - it is the unlabelled words left on the page. + * + * A box should only overlap one row. + * + * A warning is given if the box is on the same row as the previous box, but NOT + * on the same row as the previous blob. + * + * Any OUTLINE which overlaps the box is put into the new word. + * + * ascender chars must ascend above xht significantly + * xht chars must not rise above row xht significantly + * bl chars must not descend below baseline significantly + * descender chars must descend below baseline significantly + * + * ?? Certain chars are DROPPED - to limit the training data. + * + *************************************************************************/ + +void apply_boxes(BLOCK_LIST *block_list //real blocks + ) { + INT16 boxfile_lineno = 0; + INT16 boxfile_charno = 0; + BOX box; //boxfile box + char ch[2]; //correct ch from boxfile + ROW *row; + ROW *prev_row = NULL; + INT16 prev_box_right = MAX_INT16; + INT16 block_id; + INT16 row_id; + INT16 box_count = 0; + INT16 box_failures = 0; + INT16 labels_ok; + INT16 rows_ok; + INT16 bad_blobs; + INT16 tgt_char_counts[128]; //No. of box samples + // INT16 labelled_char_counts[128]; //No. of unique labelled samples + INT16 i; + INT16 rebalance_count = 0; + char min_char; + INT16 min_samples; + INT16 final_labelled_blob_count; + + for (i = 0; i < 128; i++) + tgt_char_counts[i] = 0; + + FILE* box_file; + STRING filename = imagefile; + filename += ".box"; + if (!(box_file = fopen (filename.string(), "r"))) { + CANTOPENFILE.error ("read_next_box", EXIT, + "Cant open box file %s %d", + filename.string(), errno); + } + + ch[1] = '\0'; + clear_any_old_text(block_list); + while (read_next_box (box_file, &box, &ch[0])) { + box_count++; + tgt_char_counts[ch[0]]++; + row = find_row_of_box (block_list, box, block_id, row_id); + if (box.left () < prev_box_right) { + boxfile_lineno++; + boxfile_charno = 1; + } + else + boxfile_charno++; + + if (row == NULL) { + box_failures++; + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! box overlaps no blobs or blobs in multiple rows"); + } + else { + if ((box.left () >= prev_box_right) && (row != prev_row)) + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "WARNING! false row break"); + box_failures += resegment_box (row, box, ch, block_id, row_id, + boxfile_lineno, boxfile_charno); + prev_row = row; + } + prev_box_right = box.right (); + } + tidy_up(block_list, + labels_ok, + rows_ok, + bad_blobs, + tgt_char_counts, + rebalance_count, + min_char, + min_samples, + final_labelled_blob_count); + tprintf ("APPLY_BOXES:\n"); + tprintf (" Boxes read from boxfile: %6d\n", box_count); + tprintf (" Initially labelled blobs: %6d in %d rows\n", + labels_ok, rows_ok); + tprintf (" Box failures detected: %6d\n", box_failures); + tprintf (" Duped blobs for rebalance:%6d\n", rebalance_count); + tprintf (" \"%c\" has fewest samples:%6d\n", min_char, min_samples); + tprintf (" Total unlabelled words: %6d\n", + bad_blobs); + tprintf (" Final labelled words: %6d\n", + final_labelled_blob_count); +} + + +void clear_any_old_text( //remove correct text + BLOCK_LIST *block_list //real blocks + ) { + BLOCK_IT block_it(block_list); + ROW_IT row_it; + WERD_IT word_it; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + word_it.set_to_list (row_it.data ()->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word_it.data ()->set_text (""); + } + } + } +} + + +BOOL8 read_next_box(FILE* box_file, // + BOX *box, + char *ch) { + char buff[256]; //boxfile read buffer + char *buffptr = buff; + STRING box_filename; + static INT16 line = 0; + INT32 x_min; + INT32 y_min; + INT32 x_max; + INT32 y_max; + INT32 count = 0; + + while (!feof (box_file)) { + fgets (buff, sizeof (buff) - 1, box_file); + line++; + + /* Check for blank lines in box file */ + for (buffptr = buff; isspace (*buffptr); buffptr++) + ; + if (*buffptr != '\0') { + count = + sscanf (buff, + "%c " INT32FORMAT " " INT32FORMAT " " INT32FORMAT " " + INT32FORMAT, ch, &x_min, &y_min, &x_max, &y_max); + if (count != 5) { + tprintf ("Box file format error on line %i ignored\n", line); + } + else { + *box = BOX (ICOORD (x_min, y_min), ICOORD (x_max, y_max)); + return TRUE; //read a box ok + } + } + } + return FALSE; //EOF +} + + +ROW *find_row_of_box( // + BLOCK_LIST *block_list, //real blocks + BOX box, //from boxfile + INT16 &block_id, + INT16 &row_id_to_process) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + ROW *row_to_process = NULL; + INT16 row_id; + WERD_IT word_it; + WERD *word; + BOOL8 polyg; + PBLOB_IT blob_it; + PBLOB *blob; + OUTLINE_IT outline_it; + OUTLINE *outline; + + /* + Find row to process - error if box REALLY overlaps more than one row. (I.e + it overlaps blobs in the row - not just overlaps the bounding box of the + whole row.) + */ + + block_id = 0; + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block_id++; + row_id = 0; + block = block_it.data (); + if (block->bounding_box ().overlap (box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row_id++; + row = row_it.data (); + if (row->bounding_box ().overlap (box)) { + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + polyg = word->flag (W_POLYGON); + if (word->bounding_box ().overlap (box)) { + blob_it.set_to_list (word->gblob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (gblob_bounding_box (blob, polyg). + overlap (box)) { + outline_it. + set_to_list (gblob_out_list + (blob, polyg)); + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); + outline_it.forward ()) { + outline = outline_it.data (); + if (goutline_bounding_box + (outline, polyg).major_overlap (box)) { + if ((row_to_process == NULL) || + (row_to_process == row)) { + row_to_process = row; + row_id_to_process = row_id; + } + else + /* RETURN ERROR Box overlaps blobs in more than one row */ + return NULL; + } + } + } + } + } + } + } + } + } + } + return row_to_process; +} + + +INT16 resegment_box( // + ROW *row, + BOX box, + char *ch, + INT16 block_id, + INT16 row_id, + INT16 boxfile_lineno, + INT16 boxfile_charno) { + WERD_IT word_it; + WERD *word; + WERD *new_word = NULL; + BOOL8 polyg = false; + PBLOB_IT blob_it; + PBLOB_IT new_blob_it; + PBLOB *blob; + PBLOB *new_blob; + OUTLINE_IT outline_it; + OUTLINE_LIST dummy; // Just to initialize new_outline_it. + OUTLINE_IT new_outline_it = &dummy; + OUTLINE *outline; + BOX new_word_box; + float word_x_centre; + float baseline; + INT16 error_count = 0; //number of chars lost + + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + polyg = word->flag (W_POLYGON); + if (word->bounding_box ().overlap (box)) { + blob_it.set_to_list (word->gblob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (gblob_bounding_box (blob, polyg).overlap (box)) { + outline_it.set_to_list (gblob_out_list (blob, polyg)); + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); outline_it.forward ()) { + outline = outline_it.data (); + if (goutline_bounding_box (outline, polyg). + major_overlap (box)) { + if (strlen (word->text ()) > 0) { + if (error_count == 0) { + error_count = 1; + if (applybox_debug > 4) + report_failed_box (boxfile_lineno, + boxfile_charno, + box, ch, + "FAILURE! box overlaps blob in labelled word"); + } + if (applybox_debug > 4) + tprintf + ("APPLY_BOXES: ALSO ignoring corrupted char blk:%d row:%d \"%s\"\n", + block_id, row_id, + word_it.data ()->text ()); + word_it.data ()->set_text (""); + //UN label it + error_count++; + } + + if (error_count == 0) { + if (new_word == NULL) { + /* Make a new word with a single blob */ + new_word = word->shallow_copy (); + new_word->set_text (ch); + if (polyg) + new_blob = new PBLOB; + else + new_blob = (PBLOB *) new C_BLOB; + new_blob_it.set_to_list (new_word-> + gblob_list ()); + new_blob_it.add_to_end (new_blob); + new_outline_it. + set_to_list (gblob_out_list + (new_blob, polyg)); + } + new_outline_it.add_to_end (outline_it. + extract ()); + //move blob + } + } + } + //no outlines in blob + if (outline_it.empty ()) + //so delete blob + delete blob_it.extract (); + } + } + if (blob_it.empty ()) //no blobs in word + //so delete word + delete word_it.extract (); + } + } + if (error_count > 0) + return error_count; + + if (new_word != NULL) { + gblob_sort_list (new_word->gblob_list (), polyg); + word_it.add_to_end (new_word); + new_word_box = new_word->bounding_box (); + word_x_centre = (new_word_box.left () + new_word_box.right ()) / 2.0f; + baseline = row->base_line (word_x_centre); + + if (STRING (chs_caps_ht).contains (ch[0]) && + (new_word_box.top () < + baseline + (1 + applybox_error_band) * row->x_height ())) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! caps-ht char didn't ascend"); + new_word->set_text (""); + return 1; + } + if (STRING (chs_odd_top).contains (ch[0]) && + (new_word_box.top () < + baseline + (1 - applybox_error_band) * row->x_height ())) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! Odd top char below xht"); + new_word->set_text (""); + return 1; + } + if (STRING (chs_x_ht).contains (ch[0]) && + ((new_word_box.top () > + baseline + (1 + applybox_error_band) * row->x_height ()) || + (new_word_box.top () < + baseline + (1 - applybox_error_band) * row->x_height ()))) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! x-ht char didn't have top near xht"); + new_word->set_text (""); + return 1; + } + if (STRING (chs_non_ambig_bl).contains (ch[0]) && + ((new_word_box.bottom () < + baseline - applybox_error_band * row->x_height ()) || + (new_word_box.bottom () > + baseline + applybox_error_band * row->x_height ()))) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! non ambig BL char didnt have bottom near baseline"); + new_word->set_text (""); + return 1; + } + if (STRING (chs_odd_bot).contains (ch[0]) && + (new_word_box.bottom () > + baseline + applybox_error_band * row->x_height ())) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! Odd bottom char above baseline"); + new_word->set_text (""); + return 1; + } + if (STRING (chs_desc).contains (ch[0]) && + (new_word_box.bottom () > + baseline - applybox_error_band * row->x_height ())) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! Descender doesn't descend"); + new_word->set_text (""); + return 1; + } + return 0; + } + else { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! Couldn't find any blobs"); + return 1; + } +} + + +/************************************************************************* + * tidy_up() + * - report >1 block + * - sort the words in each row. + * - report any rows with no labelled words. + * - report any remaining unlabelled words + * - report total labelled words + * + *************************************************************************/ +void tidy_up( // + BLOCK_LIST *block_list, //real blocks + INT16 &ok_char_count, + INT16 &ok_row_count, + INT16 &unlabelled_words, + INT16 *tgt_char_counts, + INT16 &rebalance_count, + char &min_char, + INT16 &min_samples, + INT16 &final_labelled_blob_count) { + BLOCK_IT block_it(block_list); + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + WERD *duplicate_word; + INT16 block_idx = 0; + INT16 row_idx; + INT16 all_row_idx = 0; + BOOL8 row_ok; + BOOL8 rebalance_needed = FALSE; + //No. of unique labelled samples + INT16 labelled_char_counts[128]; + INT16 i; + char ch; + char prev_ch = '\0'; + BOOL8 at_dupe_of_prev_word; + ROW *prev_row = NULL; + INT16 left; + INT16 prev_left = -1; + + for (i = 0; i < 128; i++) + labelled_char_counts[i] = 0; + + ok_char_count = 0; + ok_row_count = 0; + unlabelled_words = 0; + if ((applybox_debug > 4) && (block_it.length () != 1)) + + tprintf ("APPLY_BOXES: More than one block??\n"); + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block_idx++; + row_idx = 0; + row_ok = FALSE; + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row_idx++; + all_row_idx++; + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + word_it.sort (word_comparator); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (strlen (word->text ()) == 0) { + unlabelled_words++; + if (applybox_debug > 4) { + tprintf + ("APPLY_BOXES: Unlabelled word blk:%d row:%d allrows:%d\n", + block_idx, row_idx, all_row_idx); + } + } + else { + if (word->gblob_list ()->length () != 1) + tprintf + ("APPLY_BOXES: FATALITY - MULTIBLOB Labelled word blk:%d row:%d allrows:%d\n", + block_idx, row_idx, all_row_idx); + + ok_char_count++; + labelled_char_counts[*word->text ()]++; + row_ok = TRUE; + } + } + if ((applybox_debug > 4) && (!row_ok)) { + tprintf + ("APPLY_BOXES: Row with no labelled words blk:%d row:%d allrows:%d\n", + block_idx, row_idx, all_row_idx); + } + else + ok_row_count++; + } + } + + min_samples = 9999; + for (i = 0; i < 128; i++) { + if (tgt_char_counts[i] > labelled_char_counts[i]) { + if (labelled_char_counts[i] <= 1) { + tprintf + ("APPLY_BOXES: FATALITY - %d labelled samples of \"%c\" - target is %d\n", + labelled_char_counts[i], (char) i, tgt_char_counts[i]); + } + else { + rebalance_needed = TRUE; + if (applybox_debug > 0) + tprintf + ("APPLY_BOXES: REBALANCE REQD \"%c\" - target of %d from %d labelled samples\n", + (char) i, tgt_char_counts[i], labelled_char_counts[i]); + } + } + if ((min_samples > labelled_char_counts[i]) && (tgt_char_counts[i] > 0)) { + min_samples = labelled_char_counts[i]; + min_char = (char) i; + } + } + + while (applybox_rebalance && rebalance_needed) { + block_it.set_to_list (block_list); + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + left = word->bounding_box ().left (); + ch = *word->text (); + at_dupe_of_prev_word = ((row == prev_row) && + (left = prev_left) && + (ch == prev_ch)); + if ((ch != '\0') && + (labelled_char_counts[ch] > 1) && + (tgt_char_counts[ch] > labelled_char_counts[ch]) && + (!at_dupe_of_prev_word)) { + /* Duplicate the word to rebalance the labelled samples */ + if (applybox_debug > 9) { + tprintf ("Duping \"%c\" from ", ch); + word->bounding_box ().print (); + } + duplicate_word = new WERD; + *duplicate_word = *word; + word_it.add_after_then_move (duplicate_word); + rebalance_count++; + labelled_char_counts[ch]++; + } + prev_row = row; + prev_left = left; + prev_ch = ch; + } + } + } + rebalance_needed = FALSE; + for (i = 0; i < 128; i++) { + if ((tgt_char_counts[i] > labelled_char_counts[i]) && + (labelled_char_counts[i] > 1)) { + rebalance_needed = TRUE; + break; + } + } + } + + /* Now final check - count labelled blobs */ + final_labelled_blob_count = 0; + block_it.set_to_list (block_list); + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + word_it.sort (word_comparator); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if ((strlen (word->text ()) == 1) && + (word->gblob_list ()->length () == 1)) + final_labelled_blob_count++; + } + } + } +} + + +void report_failed_box(INT16 boxfile_lineno, + INT16 boxfile_charno, + BOX box, + char *box_ch, + const char *err_msg) { + if (applybox_debug > 4) + tprintf ("APPLY_BOXES: boxfile %1d/%1d/%s ((%1d,%1d),(%1d,%1d)): %s\n", + boxfile_lineno, + boxfile_charno, + box_ch, + box.left (), box.bottom (), box.right (), box.top (), err_msg); +} + + +void apply_box_training(BLOCK_LIST *block_list) { + BLOCK_IT block_it(block_list); + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + WERD *bln_word; + WERD copy_outword; // copy to denorm + PBLOB_IT blob_it; + DENORM denorm; + INT16 count = 0; + char ch[2]; + + ch[1] = '\0'; + + tprintf ("Generating training data\n"); + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if ((strlen (word->text ()) == 1) && + (word->gblob_list ()->length () == 1)) { + /* Here is a word with a single char label and a single blob so train on it */ + bln_word = + make_bln_copy (word, row, row->x_height (), &denorm); + blob_it.set_to_list (bln_word->blob_list ()); + ch[0] = *word->text (); + tess_training_tester (blob_it.data (), + //single blob + &denorm, TRUE, //correct + ch, //correct ASCII char + 1, //ASCII length + NULL); + copy_outword = *(bln_word); + copy_outword.baseline_denormalise (&denorm); + blob_it.set_to_list (copy_outword.blob_list ()); + ch[0] = *word->text (); + delete bln_word; + count++; + } + } + } + } + tprintf ("Generated training data for %d blobs\n", count); +} + + +void apply_box_testing(BLOCK_LIST *block_list) { + BLOCK_IT block_it(block_list); + ROW_IT row_it; + ROW *row; + INT16 row_count = 0; + WERD_IT word_it; + WERD *word; + WERD *bln_word; + INT16 word_count = 0; + PBLOB_IT blob_it; + DENORM denorm; + INT16 count = 0; + char ch[2]; + WERD *outword; //bln best choice + //segmentation + WERD_CHOICE *best_choice; //tess output + WERD_CHOICE *raw_choice; //top choice permuter + //detailed results + BLOB_CHOICE_LIST_CLIST blob_choices; + INT16 char_count = 0; + INT16 correct_count = 0; + INT16 err_count = 0; + INT16 rej_count = 0; + #ifndef SECURE_NAMES + WERDSTATS wordstats; //As from newdiff + #endif + char tess_rej_str[3]; + char tess_long_str[3]; + + ch[1] = '\0'; + strcpy (tess_rej_str, "|A"); + strcpy (tess_long_str, "|B"); + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + row_count++; + word_count = 0; + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + word_count++; + if ((strlen (word->text ()) == 1) && + !STRING (applybox_test_exclusions).contains (*word->text ()) + && (word->gblob_list ()->length () == 1)) { + /* Here is a word with a single char label and a single blob so test it */ + bln_word = + make_bln_copy (word, row, row->x_height (), &denorm); + blob_it.set_to_list (bln_word->blob_list ()); + ch[0] = *word->text (); + char_count++; + best_choice = tess_segment_pass1 (bln_word, + &denorm, + tess_default_matcher, + raw_choice, + &blob_choices, outword); + + /* + Test for TESS screw up on word. Recog_word has already ensured that the + choice list, outword blob lists and best_choice string are the same + length. A TESS screw up is indicated by a blank filled or 0 length string. + */ + if ((best_choice->string ().length () == 0) || + (strspn (best_choice->string ().string (), " ") == + best_choice->string ().length ())) { + rej_count++; + tprintf ("%d:%d: \"%s\" -> TESS FAILED\n", + row_count, word_count, ch); + #ifndef SECURE_NAMES + wordstats.word (tess_rej_str, 2, ch, 1); + #endif + } + else { + if ((best_choice->string ().length () != + outword->blob_list ()->length ()) || + (best_choice->string ().length () != + blob_choices.length ())) { + tprintf + ("ASSERT FAIL String:\"%s\"; Strlen=%d; #Blobs=%d; #Choices=%d\n", + best_choice->string ().string (), + best_choice->string ().length (), + outword->blob_list ()->length (), + blob_choices.length ()); + } + ASSERT_HOST (best_choice->string ().length () == + outword->blob_list ()->length ()); + ASSERT_HOST (best_choice->string ().length () == + blob_choices.length ()); + fix_quotes ((char *) best_choice->string ().string (), + //turn to double + outword, &blob_choices); + if (strcmp (best_choice->string ().string (), ch) != 0) { + err_count++; + tprintf ("%d:%d: \"%s\" -> \"%s\"\n", + row_count, word_count, ch, + best_choice->string ().string ()); + } + else + correct_count++; + #ifndef SECURE_NAMES + if (best_choice->string ().length () > 2) + wordstats.word (tess_long_str, 2, ch, 1); + else + wordstats.word ((char *) best_choice->string (). + string (), + best_choice->string ().length (), ch, + 1); + #endif + } + delete bln_word; + delete outword; + delete best_choice; + delete raw_choice; + blob_choices.deep_clear (); + count++; + } + } + } + } + #ifndef SECURE_NAMES + wordstats.print (1, 100.0); + wordstats.conf_matrix (); + tprintf ("Tested %d chars: %d correct; %d rejected by tess; %d errs\n", + char_count, correct_count, rej_count, err_count); + #endif +} diff --git a/ccmain/applybox.h b/ccmain/applybox.h new file mode 100644 index 0000000000..14533f2f59 --- /dev/null +++ b/ccmain/applybox.h @@ -0,0 +1,71 @@ +/********************************************************************** + * File: applybox.h (Formerly applybox.h) + * Description: Re segment rows according to box file data + * Author: Phil Cheatle + * Created: Wed Nov 24 09:11:23 GMT 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef APPLYBOX_H +#define APPLYBOX_H + +#include "varable.h" +#include "ocrblock.h" +#include "ocrrow.h" +#include "notdll.h" + +extern BOOL_VAR_H (applybox_rebalance, TRUE, "Drop dead"); +extern INT_VAR_H (applybox_debug, 0, "Debug level"); +extern STRING_VAR_H (applybox_test_exclusions, "|", +"Chars ignored for testing"); +extern double_VAR_H (applybox_error_band, 0.15, "Err band as fract of xht"); +void apply_boxes(BLOCK_LIST *block_list //real blocks + ); +void clear_any_old_text( //remove correct text + BLOCK_LIST *block_list //real blocks + ); +BOOL8 read_next_box(FILE* box_file, // + BOX *box, + char *ch); +ROW *find_row_of_box( // + BLOCK_LIST *block_list, //real blocks + BOX box, //from boxfile + INT16 &block_id, + INT16 &row_id_to_process); +INT16 resegment_box( // + ROW *row, + BOX box, + char *ch, + INT16 block_id, + INT16 row_id, + INT16 boxfile_lineno, + INT16 boxfile_charno); +void tidy_up( // + BLOCK_LIST *block_list, //real blocks + INT16 &ok_char_count, + INT16 &ok_row_count, + INT16 &unlabelled_words, + INT16 *tgt_char_counts, + INT16 &rebalance_count, + char &min_char, + INT16 &min_samples, + INT16 &final_labelled_blob_count); +void report_failed_box(INT16 boxfile_lineno, + INT16 boxfile_charno, + BOX box, + char *box_ch, + const char *err_msg); +void apply_box_training(BLOCK_LIST *block_list); +void apply_box_testing(BLOCK_LIST *block_list); +#endif diff --git a/ccmain/baseapi.cpp b/ccmain/baseapi.cpp new file mode 100644 index 0000000000..0d76398767 --- /dev/null +++ b/ccmain/baseapi.cpp @@ -0,0 +1,395 @@ +/********************************************************************** + * File: baseapi.cpp + * Description: Simple API for calling tesseract. + * Author: Ray Smith + * Created: Fri Oct 06 15:35:01 PDT 2006 + * + * (C) Copyright 2006, Google Inc. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "baseapi.h" + +#include "tessedit.h" +#include "pageres.h" +#include "tessvars.h" +#include "control.h" +#include "applybox.h" +#include "pgedit.h" +#include "varabled.h" +#include "adaptmatch.h" + +BOOL_VAR(tessedit_resegment_from_boxes, FALSE, + "Take segmentation and labeling from box file"); +BOOL_VAR(tessedit_train_from_boxes, FALSE, + "Generate training data from boxed chars"); + +// Minimum sensible image size to be worth running tesseract. +const int kMinRectSize = 10; + +// Start tesseract. +// The datapath must be the name of the data directory or some other file +// in which the data directory resides (for instance argv[0].) +// The configfile is the name of a file in the tessconfigs directory +// (eg batch) or NULL to run on defaults. +// Outputbase may also be NULL, and is the basename of various output files. +// If the output of any of these files is enabled, then a name nmust be given. +// If numeric_mode is true, only possible digits and roman numbers are +// returned. Returns 0 if successful. Crashes if not. +// The argc and argv may be 0 and NULL respectively. They are used for +// providing config files for debug/display purposes. +// TODO(rays) get the facts straight. Is it OK to call +// it more than once? Make it properly check for errors and return them. +int TessBaseAPI::Init(const char* datapath, const char* outputbase, + const char* configfile, bool numeric_mode, + int argc, char* argv[]) { + int result = init_tesseract(datapath, outputbase, configfile, argc, argv); + bln_numericmode.set_value(numeric_mode); + return result; +} + +// Recognize a rectangle from an image and return the result as a string. +// May be called many times for a single Init. +// Currently has no error checking. +// Greyscale of 8 and color of 24 or 32 bits per pixel may be given. +// Palette color images will not work properly and must be converted to +// 24 bit. +// Binary images of 1 bit per pixel may also be given but they must be +// byte packed with the MSB of the first byte being the first pixel, and a +// one pixel is WHITE. For binary images set bytes_per_pixel=0. +// The recognized text is returned as a char* which (in future will be coded +// as UTF8 and) must be freed with the delete [] operator. +char* TessBaseAPI::TesseractRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, + int width, int height) { + if (width < kMinRectSize || height < kMinRectSize) + return NULL; // Nothing worth doing. + + // Copy/Threshold the image to the tesseract global page_image. + CopyImageToTesseract(imagedata, bytes_per_pixel, bytes_per_line, + left, top, width, height); + + return RecognizeToString(); +} + +// Call between pages or documents etc to free up memory and forget +// adaptive data. +void TessBaseAPI::ClearAdaptiveClassifier() { + ResetAdaptiveClassifier(); +} + +// Close down tesseract and free up memory. +void TessBaseAPI::End() { + ResetAdaptiveClassifier(); + end_tesseract(); +} + +// Dump the internal binary image to a PGM file. +void TessBaseAPI::DumpPGM(const char* filename) { + IMAGELINE line; + line.init(page_image.get_xsize()); + FILE *fp = fopen(filename, "w"); + fprintf(fp, "P5 " INT32FORMAT " " INT32FORMAT " 255\n", page_image.get_xsize(), + page_image.get_ysize()); + for (int j = page_image.get_ysize()-1; j >= 0 ; --j) { + page_image.get_line(0, j, page_image.get_xsize(), &line, 0); + for (int i = 0; i < page_image.get_xsize(); ++i) { + UINT8 b = line.pixels[i] ? 255 : 0; + fwrite(&b, 1, 1, fp); + } + } + fclose(fp); +} + +// Copy the given image rectangle to Tesseract, with adaptive thresholding +// if the image is not already binary. +void TessBaseAPI::CopyImageToTesseract(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, + int width, int height) { + if (bytes_per_pixel > 0) { + // Threshold grey or color. + int* thresholds = new int[bytes_per_pixel]; + int* hi_values = new int[bytes_per_pixel]; + + // Compute the thresholds. + OtsuThreshold(imagedata, bytes_per_pixel, bytes_per_line, + left, top, left + width, top + height, + thresholds, hi_values); + + // Threshold the image to the tesseract global page_image. + ThresholdRect(imagedata, bytes_per_pixel, bytes_per_line, + left, top, width, height, + thresholds, hi_values); + delete [] thresholds; + delete [] hi_values; + } else { + CopyBinaryRect(imagedata, bytes_per_line, left, top, width, height); + } +} + +// Compute the Otsu threshold(s) for the given image rectangle, making one +// for each channel. Each channel is always one byte per pixel. +// Returns an array of threshold values and an array of hi_values, such +// that a pixel value >threshold[channel] is considered foreground if +// hi_values[channel] is 0 or background if 1. A hi_value of -1 indicates +// that there is no apparent foreground. At least one hi_value will not be -1. +// thresholds and hi_values are assumed to be of bytes_per_pixel size. +void TessBaseAPI::OtsuThreshold(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int right, int bottom, + int* thresholds, + int* hi_values) { + // Of all channels with no good hi_value, keep the best so we can always + // produce at least one answer. + int best_hi_value = 0; + int best_hi_index = 0; + bool any_good_hivalue = false; + double best_hi_dist = 0.0; + + for (int ch = 0; ch < bytes_per_pixel; ++ch) { + thresholds[ch] = 0; + hi_values[ch] = -1; + // Compute the histogram of the image rectangle. + int histogram[256]; + HistogramRect(imagedata + ch, bytes_per_pixel, bytes_per_line, + left, top, right, bottom, histogram); + int H; + int best_omega_0; + int best_t = OtsuStats(histogram, &H, &best_omega_0); + // To be a convincing foreground we must have a small fraction of H + // or to be a convincing background we must have a large fraction of H. + // In between we assume this channel contains no thresholding information. + int hi_value = best_omega_0 < H * 0.5; + thresholds[ch] = best_t; + if (best_omega_0 > H * 0.75) { + any_good_hivalue = true; + hi_values[ch] = 0; + } + else if (best_omega_0 < H * 0.25) { + any_good_hivalue = true; + hi_values[ch] = 1; + } + else { + // In case all channels are like this, keep the best of the bad lot. + double hi_dist = hi_value ? (H - best_omega_0) : best_omega_0; + if (hi_dist > best_hi_dist) { + best_hi_dist = hi_dist; + best_hi_value = hi_value; + best_hi_index = ch; + } + } + } + if (!any_good_hivalue) { + // Use the best of the ones that were not good enough. + hi_values[best_hi_index] = best_hi_value; + } +} + +// Compute the histogram for the given image rectangle, and the given +// channel. (Channel pointed to by imagedata.) Each channel is always +// one byte per pixel. +// Bytes per pixel is used to skip channels not being +// counted with this call in a multi-channel (pixel-major) image. +// Histogram is always a 256 element array to count occurrences of +// each pixel value. +void TessBaseAPI::HistogramRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int right, int bottom, + int* histogram) { + int width = right - left; + memset(histogram, 0, sizeof(*histogram) * 256); + const UINT8* pix = imagedata + + top*bytes_per_line + + left*bytes_per_pixel; + for (int y = top; y < bottom; ++y) { + for (int x = 0; x < width; ++x) { + ++histogram[pix[x * bytes_per_pixel]]; + } + pix += bytes_per_line; + } +} + +// Compute the Otsu threshold(s) for the given histogram. +// Also returns H = total count in histogram, and +// omega0 = count of histogram below threshold. +int TessBaseAPI::OtsuStats(const int* histogram, + int* H_out, + int* omega0_out) { + int H = 0; + double mu_T = 0.0; + for (int i = 0; i < 256; ++i) { + H += histogram[i]; + mu_T += i * histogram[i]; + } + + // Now maximize sig_sq_B over t. + // http://www.ctie.monash.edu.au/hargreave/Cornall_Terry_328.pdf + int best_t = -1; + int omega_0, omega_1; + int best_omega_0 = 0; + double best_sig_sq_B = 0.0; + double mu_0, mu_1, mu_t; + omega_0 = 0; + mu_t = 0.0; + for (int t = 0; t < 255; ++t) { + omega_0 += histogram[t]; + mu_t += t * static_cast(histogram[t]); + if (omega_0 == 0) + continue; + omega_1 = H - omega_0; + mu_0 = mu_t / omega_0; + mu_1 = (mu_T - mu_t) / omega_1; + double sig_sq_B = mu_1 - mu_0; + sig_sq_B *= sig_sq_B * omega_0 * omega_1; + if (best_t < 0 || sig_sq_B > best_sig_sq_B) { + best_sig_sq_B = sig_sq_B; + best_t = t; + best_omega_0 = omega_0; + } + } + if (H_out != NULL) *H_out = H; + if (omega0_out != NULL) *omega0_out = best_omega_0; + return best_t; +} + +// Threshold the given grey or color image into the tesseract global +// image ready for recognition. Requires thresholds and hi_value +// produced by OtsuThreshold above. +void TessBaseAPI::ThresholdRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, + int width, int height, + const int* thresholds, + const int* hi_values) { + IMAGELINE line; + page_image.create(width, height, 1); + line.init(width); + // For each line in the image, fill the IMAGELINE class and put it into the + // Tesseract global page_image. Note that Tesseract stores images with the + // bottom at y=0 and 0 is black, so we need 2 kinds of inversion. + const UINT8* data = imagedata + top*bytes_per_line + left*bytes_per_pixel; + for (int y = height - 1 ; y >= 0; --y) { + const UINT8* pix = data; + for (int x = 0; x < width; ++x, pix += bytes_per_pixel) { + line.pixels[x] = 1; + for (int ch = 0; ch < bytes_per_pixel; ++ch) { + if (hi_values[ch] >= 0 && + (pix[ch] > thresholds[ch]) == (hi_values[ch] == 0)) { + line.pixels[x] = 0; + break; + } + } + } + page_image.put_line(0, y, width, &line, 0); + data += bytes_per_line; + } +} + +// Cut out the requested rectangle of the binary image to the +// tesseract global image ready for recognition. +void TessBaseAPI::CopyBinaryRect(const UINT8* imagedata, + int bytes_per_line, + int left, int top, + int width, int height) { + // Copy binary image, cutting out the required rectangle. + IMAGE image; + image.capture(const_cast(imagedata), + bytes_per_line*8, top + height, 1); + page_image.create(width, height, 1); + copy_sub_image(&image, left, top, width, height, &page_image, 0, 0, false); +} + +// Low-level function to recognize the current global image to a string. +char* TessBaseAPI::RecognizeToString() { + BLOCK_LIST block_list; + + FindLines(&block_list); + + // Now run the main recognition. + PAGE_RES* page_res = Recognize(&block_list, NULL); + + return TesseractToText(page_res); +} + +// Find lines from the image making the BLOCK_LIST. +void TessBaseAPI::FindLines(BLOCK_LIST* block_list) { + STRING input_file = "noname.tif"; + // The following call creates a full-page block and then runs connected + // component analysis and text line creation. + pgeditor_read_file(input_file, block_list); +} + +// Recognize the tesseract global image and return the result as Tesseract +// internal structures. +PAGE_RES* TessBaseAPI::Recognize(BLOCK_LIST* block_list, ETEXT_DESC* monitor) { + if (tessedit_resegment_from_boxes) + apply_boxes(block_list); + if (edit_variables) + start_variables_editor(); + + PAGE_RES* page_res = new PAGE_RES(block_list); + if (interactive_mode) { + pgeditor_main(block_list); //pgeditor user I/F + } else if (tessedit_train_from_boxes) { + apply_box_training(block_list); + } else { + // Now run the main recognition. + recog_all_words(page_res, monitor); + } + return page_res; +} + +// Make a text string from the internal data structures. +// The input page_res is deleted. +char* TessBaseAPI::TesseractToText(PAGE_RES* page_res) { + if (page_res != NULL) { + int total_length = 2; + PAGE_RES_IT page_res_it(page_res); + // Iterate over the data structures to extract the recognition result. + for (page_res_it.restart_page(); page_res_it.word () != NULL; + page_res_it.forward()) { + WERD_RES *word = page_res_it.word(); + WERD_CHOICE* choice = word->best_choice; + if (choice != NULL) { + total_length += choice->string().length() + 1; + } + } + char* result = new char[total_length]; + char* ptr = result; + for (page_res_it.restart_page(); page_res_it.word () != NULL; + page_res_it.forward()) { + WERD_RES *word = page_res_it.word(); + WERD_CHOICE* choice = word->best_choice; + if (choice != NULL) { + strcpy(ptr, choice->string().string()); + ptr += strlen(ptr); + if (word->word->flag(W_EOL)) + *ptr++ = '\n'; + else + *ptr++ = ' '; + } + } + *ptr++ = '\n'; + *ptr = '\0'; + delete page_res; + return result; + } + return NULL; +} + diff --git a/ccmain/baseapi.h b/ccmain/baseapi.h new file mode 100644 index 0000000000..591d415d8d --- /dev/null +++ b/ccmain/baseapi.h @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////// +// File: baseapi.h +// Description: Simple API for calling tesseract. +// Author: Ray Smith +// Created: Fri Oct 06 15:35:01 PDT 2006 +// +// (C) Copyright 2006, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef THIRD_PARTY_TESSERACT_CCMAIN_BASEAPI_H__ +#define THIRD_PARTY_TESSERACT_CCMAIN_BASEAPI_H__ + +#include + +#include "host.h" +#include "ocrclass.h" + +class PAGE_RES; +class BLOCK_LIST; + +// Base class for all tesseract APIs. +// Specific classes can add ability to work on different inputs or produce +// different outputs. + +class TessBaseAPI { + public: + // Start tesseract. + // The datapath must be the name of the data directory or some other file + // in which the data directory resides (for instance argv[0].) + // The configfile is the name of a file in the tessconfigs directory + // (eg batch) or NULL to run on defaults. + // Outputbase may also be NULL, and is the basename of various output files. + // If the output of any of these files is enabled, then a name must be given. + // If numeric_mode is true, only possible digits and roman numbers are + // returned. Returns 0 if successful. Crashes if not. + // The argc and argv may be 0 and NULL respectively. They are used for + // providing config files for debug/display purposes. + // TODO(rays) get the facts straight. Is it OK to call + // it more than once? Make it properly check for errors and return them. + static int Init(const char* datapath, const char* outputbase, + const char* configfile, bool numeric_mode, + int argc, char* argv[]); + + // Recognize a rectangle from an image and return the result as a string. + // May be called many times for a single Init. + // Currently has no error checking. + // Greyscale of 8 and color of 24 or 32 bits per pixel may be given. + // Palette color images will not work properly and must be converted to + // 24 bit. + // Binary images of 1 bit per pixel may also be given but they must be + // byte packed with the MSB of the first byte being the first pixel, and a + // 1 represents WHITE. For binary images set bytes_per_pixel=0. + // The recognized text is returned as a char* which (in future will be coded + // as UTF8 and) must be freed with the delete [] operator. + static char* TesseractRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int width, int height); + + // Call between pages or documents etc to free up memory and forget + // adaptive data. + static void ClearAdaptiveClassifier(); + + // Close down tesseract and free up memory. + static void End(); + + // Dump the internal binary image to a PGM file. + static void DumpPGM(const char* filename); + + protected: + // Copy the given image rectangle to Tesseract, with adaptive thresholding + // if the image is not already binary. + static void CopyImageToTesseract(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int width, int height); + + // Compute the Otsu threshold(s) for the given image rectangle, making one + // for each channel. Each channel is always one byte per pixel. + // Returns an array of threshold values and an array of hi_values, such + // that a pixel value >threshold[channel] is considered foreground if + // hi_values[channel] is 0 or background if 1. A hi_value of -1 indicates + // that there is no apparent foreground. At least one hi_value will not be -1. + // thresholds and hi_values are assumed to be of bytes_per_pixel size. + static void OtsuThreshold(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int right, int bottom, + int* thresholds, + int* hi_values); + + // Compute the histogram for the given image rectangle, and the given + // channel. (Channel pointed to by imagedata.) Each channel is always + // one byte per pixel. + // Bytes per pixel is used to skip channels not being + // counted with this call in a multi-channel (pixel-major) image. + // Histogram is always a 256 element array to count occurrences of + // each pixel value. + static void HistogramRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int right, int bottom, + int* histogram); + + // Compute the Otsu threshold(s) for the given histogram. + // Also returns H = total count in histogram, and + // omega0 = count of histogram below threshold. + static int OtsuStats(const int* histogram, + int* H_out, + int* omega0_out); + + // Threshold the given grey or color image into the tesseract global + // image ready for recognition. Requires thresholds and hi_value + // produced by OtsuThreshold above. + static void ThresholdRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, + int width, int height, + const int* thresholds, + const int* hi_values); + + // Cut out the requested rectangle of the binary image to the + // tesseract global image ready for recognition. + static void CopyBinaryRect(const UINT8* imagedata, + int bytes_per_line, + int left, int top, + int width, int height); + + // Low-level function to recognize the current global image to a string. + static char* RecognizeToString(); + + // Find lines from the image making the BLOCK_LIST. + static void FindLines(BLOCK_LIST* block_list); + + // Recognize the tesseract global image and return the result as Tesseract + // internal structures. + static PAGE_RES* Recognize(BLOCK_LIST* block_list, ETEXT_DESC* monitor); + + // Convert (and free) the internal data structures into a text string. + static char* TesseractToText(PAGE_RES* page_res); +}; + +#endif // THIRD_PARTY_TESSERACT_CCMAIN_BASEAPI_H__ diff --git a/ccmain/blobcmp.cpp b/ccmain/blobcmp.cpp new file mode 100644 index 0000000000..c0c8e34e86 --- /dev/null +++ b/ccmain/blobcmp.cpp @@ -0,0 +1,76 @@ +/********************************************************************** + * File: blobcmp.c (Formerly blobcmp.c) + * Description: Code to compare blobs using the adaptive matcher. + * Author: Ray Smith + * Created: Wed Apr 21 09:28:51 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "fxdefs.h" +#include "ocrfeatures.h" +#include "intmatcher.h" +#include "intproto.h" +#include "adaptive.h" +#include "adaptmatch.h" +#include "const.h" +#include "tessvars.h" + +#define CMP_CLASS 'x' + +/********************************************************************** + * compare_tess_blobs + * + * Match 2 blobs using the adaptive classifier. + **********************************************************************/ +float compare_tess_blobs(TBLOB *blob1, + TEXTROW *row1, + TBLOB *blob2, + TEXTROW *row2) { + int fcount; /*number of features */ + ADAPT_TEMPLATES ad_templates; + LINE_STATS line_stats1, line_stats2; + INT_FEATURE_ARRAY int_features; + FEATURE_SET float_features; + INT_RESULT_STRUCT int_result; /*output */ + + BIT_VECTOR AllProtosOn = NewBitVector (MAX_NUM_PROTOS); + BIT_VECTOR AllConfigsOn = NewBitVector (MAX_NUM_CONFIGS); + set_all_bits (AllProtosOn, WordsInVectorOfSize (MAX_NUM_PROTOS)); + set_all_bits (AllConfigsOn, WordsInVectorOfSize (MAX_NUM_CONFIGS)); + + EnterClassifyMode; + ad_templates = NewAdaptedTemplates (); + GetLineStatsFromRow(row1, &line_stats1); + /*copy baseline stuff */ + GetLineStatsFromRow(row2, &line_stats2); + MakeNewAdaptedClass(blob1, &line_stats1, CMP_CLASS, ad_templates); + fcount = GetAdaptiveFeatures (blob2, &line_stats2, + int_features, &float_features); + if (fcount > 0) { + SetBaseLineMatch(); + IntegerMatcher (ClassForClassId (ad_templates->Templates, CMP_CLASS), + AllProtosOn, AllConfigsOn, fcount, fcount, + int_features, 0, 0, &int_result, testedit_match_debug); + FreeFeatureSet(float_features); + if (int_result.Rating < 0) + int_result.Rating = MAX_FLOAT32; + } + + free_adapted_templates(ad_templates); + FreeBitVector(AllConfigsOn); + FreeBitVector(AllProtosOn); + + return fcount > 0 ? int_result.Rating * fcount : MAX_FLOAT32; +} diff --git a/ccmain/blobcmp.h b/ccmain/blobcmp.h new file mode 100644 index 0000000000..8ead234128 --- /dev/null +++ b/ccmain/blobcmp.h @@ -0,0 +1,29 @@ +/********************************************************************** + * File: blobcmp.c + * Description: Code to compare blobs using the adaptive matcher. + * Author: Ray Smith + * Created: Wed Apr 21 09:28:51 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BLOBCMP_H +#define BLOBCMP_H + +#include "tstruct.h" + +float compare_tess_blobs(TBLOB *blob1, + TEXTROW *row1, + TBLOB *blob2, + TEXTROW *row2); +#endif diff --git a/ccmain/callnet.cpp b/ccmain/callnet.cpp new file mode 100644 index 0000000000..506ed57520 --- /dev/null +++ b/ccmain/callnet.cpp @@ -0,0 +1,93 @@ +/********************************************************************** + * File: callnet.cpp (Formerly callnet.c) + * Description: Interface to Neural Net matcher + * Author: Phil Cheatle + * Created: Wed Nov 18 10:35:00 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "errcode.h" +//#include "nmatch.h" +#include "globals.h" + +#define OUTPUT_NODES 94 + +const ERRCODE NETINIT = "NN init error"; + +//extern "C" +//{ +//extern char* demodir; /* where program lives */ + +void init_net() { /* Initialise net */ +#ifdef ASPIRIN_INCLUDED + char wts_filename[256]; + + if (nmatch_init_network () != 0) { + NETINIT.error ("Init_net", EXIT, "Errcode %s", nmatch_error_string ()); + } + strcpy(wts_filename, demodir); + strcat (wts_filename, "tessdata/netwts"); + + if (nmatch_load_network (wts_filename) != 0) { + NETINIT.error ("Init_net", EXIT, "Weights failed, Errcode %s", + nmatch_error_string ()); + } +#endif +} + + +void callnet( /* Apply image to net */ + float *input_vector, + char *top, + float *top_score, + char *next, + float *next_score) { +#ifdef ASPIRIN_INCLUDED + float *output_vector; + int i; + int max_out_i = 0; + int next_max_out_i = 0; + float max_out = -9; + float next_max_out = -9; + + nmatch_set_input(input_vector); + nmatch_propagate_forward(); + output_vector = nmatch_get_output (); + + /* Now find top two choices */ + + for (i = 0; i < OUTPUT_NODES; i++) { + if (output_vector[i] > max_out) { + next_max_out = max_out; + max_out = output_vector[i]; + next_max_out_i = max_out_i; + max_out_i = i; + } + else { + if (output_vector[i] > next_max_out) { + next_max_out = output_vector[i]; + next_max_out_i = i; + } + } + } + *top = max_out_i + '!'; + *next = next_max_out_i + '!'; + *top_score = max_out; + *next_score = next_max_out; +#endif +} + + +//}; diff --git a/ccmain/callnet.h b/ccmain/callnet.h new file mode 100644 index 0000000000..528bd96c75 --- /dev/null +++ b/ccmain/callnet.h @@ -0,0 +1,32 @@ +/********************************************************************** + * File: callnet.h (Formerly callnet.h) + * Description: Interface to Neural Net matcher + * Author: Phil Cheatle + * Created: Wed Nov 18 10:35:00 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef CALLNET_H +#define CALLNET_H + +// extern "C" { +void init_net(); /* Initialise net */ +void callnet( /* Apply image to net */ + float *input_vector, + char *top, + float *top_score, + char *next, + float *next_score); +// }; +#endif diff --git a/ccmain/charcut.cpp b/ccmain/charcut.cpp new file mode 100644 index 0000000000..5bbb3fa3ee --- /dev/null +++ b/ccmain/charcut.cpp @@ -0,0 +1,710 @@ +/********************************************************************** + * File: charcut.cpp (Formerly charclip.c) + * Description: Code for character clipping + * Author: Phil Cheatle + * Created: Wed Nov 11 08:35:15 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "charcut.h" +#include "imgs.h" +#include "showim.h" +#include "evnts.h" +#include "notdll.h" + +#define LARGEST(a,b) ( (a) > (b) ? (a) : (b) ) +#define SMALLEST(a,b) ( (a) > (b) ? (b) : (a) ) +#define BUG_OFFSET 1 +#define EXTERN + +EXTERN INT_VAR (pix_word_margin, 3, "How far outside word BB to grow"); + +extern IMAGE page_image; + +ELISTIZE (PIXROW) +/************************************************************************* + * PIXROW::PIXROW() + * + * Constructor for a specified size PIXROW from a blob + *************************************************************************/ +PIXROW::PIXROW(INT16 pos, INT16 count, PBLOB *blob) { + OUTLINE_LIST *outline_list; + OUTLINE_IT outline_it; + POLYPT_LIST *pts_list; + POLYPT_IT pts_it; + INT16 i; + FCOORD pt; + FCOORD vec; + float y_coord; + INT16 x_coord; + + row_offset = pos; + row_count = count; + min = (INT16 *) alloc_mem (count * sizeof (INT16)); + max = (INT16 *) alloc_mem (count * sizeof (INT16)); + outline_list = blob->out_list (); + outline_it.set_to_list (outline_list); + + for (i = 0; i < count; i++) { + min[i] = MAX_INT16 - 1; + max[i] = -MAX_INT16 + 1; + y_coord = row_offset + i + 0.5; + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); outline_it.forward ()) { + pts_list = outline_it.data ()->polypts (); + pts_it.set_to_list (pts_list); + for (pts_it.mark_cycle_pt (); + !pts_it.cycled_list (); pts_it.forward ()) { + pt = pts_it.data ()->pos; + vec = pts_it.data ()->vec; + if ((vec.y () != 0) && + (((pt.y () <= y_coord) && (pt.y () + vec.y () >= y_coord)) + || ((pt.y () >= y_coord) + && (pt.y () + vec.y () <= y_coord)))) { + /* The segment crosses y_coord so find x-point and check for min/max. */ + x_coord = (INT16) floor ((y_coord - + pt.y ()) * vec.x () / vec.y () + + pt.x () + 0.5); + if (x_coord < min[i]) + min[i] = x_coord; + x_coord--; //to get pix to left of line + if (x_coord > max[i]) + max[i] = x_coord; + } + } + } + } +} + + +/************************************************************************* + * PIXROW::plot() + * + * Draw the PIXROW + *************************************************************************/ + +#ifndef GRAPHICS_DISABLED +void PIXROW::plot(WINDOW fd //where to paint + ) const { + INT16 i; + INT16 y_coord; + + for (i = 0; i < row_count; i++) { + y_coord = row_offset + i; + if (min[i] <= max[i]) { + rectangle (fd, min[i], y_coord, max[i] + 1, y_coord + 1); + } + } +} +#endif + +/************************************************************************* + * PIXROW::bounding_box() + * + * Generate bounding box for blob image + *************************************************************************/ + +bool PIXROW::bad_box( //return true if box exceeds image + int xsize, + int ysize) const { + BOX bbox = bounding_box (); + if (bbox.left () < 0 || bbox.right () > xsize + || bbox.top () > ysize || bbox.bottom () < 0) { + tprintf("Box (%d,%d)->(%d,%d) bad compared to %d,%d\n", + bbox.left(),bbox.bottom(), bbox.right(), bbox.top(), + xsize, ysize); + return true; + } + return false; +} + + +/************************************************************************* + * PIXROW::bounding_box() + * + * Generate bounding box for blob image + *************************************************************************/ + +BOX PIXROW::bounding_box() const { + INT16 i; + INT16 y_coord; + INT16 min_x = MAX_INT16 - 1; + INT16 min_y = MAX_INT16 - 1; + INT16 max_x = -MAX_INT16 + 1; + INT16 max_y = -MAX_INT16 + 1; + + for (i = 0; i < row_count; i++) { + y_coord = row_offset + i; + if (min[i] <= max[i]) { + if (y_coord < min_y) + min_y = y_coord; + if (y_coord + 1 > max_y) + max_y = y_coord + 1; + if (min[i] < min_x) + min_x = min[i]; + if (max[i] + 1 > max_x) + max_x = max[i] + 1; + } + } + if (min_x > max_x || min_y > max_y) + return BOX (); + else + return BOX (ICOORD (min_x, min_y), ICOORD (max_x, max_y)); +} + + +/************************************************************************* + * PIXROW::contract() + * + * Reduce the mins and maxs so that they end on black pixels + *************************************************************************/ + +void PIXROW::contract( //image array + IMAGELINE *imlines, + INT16 x_offset, //of pixels[0] + INT16 foreground_colour //0 or 1 + ) { + INT16 i; + UINT8 *line_pixels; + + for (i = 0; i < row_count; i++) { + if (min[i] > max[i]) + continue; + + line_pixels = imlines[i].pixels; + while (line_pixels[min[i] - x_offset] != foreground_colour) { + if (min[i] == max[i]) { + min[i] = MAX_INT16 - 1; + max[i] = -MAX_INT16 + 1; + goto nextline; + } + else + min[i]++; + } + while (line_pixels[max[i] - x_offset] != foreground_colour) { + if (min[i] == max[i]) { + min[i] = MAX_INT16 - 1; + max[i] = -MAX_INT16 + 1; + goto nextline; + } + else + max[i]--; + } + nextline:; + //goto label! + } +} + + +/************************************************************************* + * PIXROW::extend() + * + * 1 pixel extension in each direction to cover extra black area + *************************************************************************/ + +BOOL8 PIXROW::extend( //image array + IMAGELINE *imlines, + BOX &imbox, + PIXROW *prev, //for prev blob + PIXROW *next, //for next blob + INT16 foreground_colour) { + INT16 i; + INT16 x_offset = imbox.left (); + INT16 limit; + INT16 left_limit; + INT16 right_limit; + UINT8 *pixels = NULL; + UINT8 *pixels_below = NULL; //row below current + UINT8 *pixels_above = NULL; //row above current + BOOL8 changed = FALSE; + + pixels_above = imlines[0].pixels; + for (i = 0; i < row_count; i++) { + pixels_below = pixels; + pixels = pixels_above; + if (i < (row_count - 1)) + pixels_above = imlines[i + 1].pixels; + else + pixels_above = NULL; + + /* Extend Left by one pixel*/ + if (prev == NULL || prev->max[i] < prev->min[i]) + limit = imbox.left (); + else + limit = prev->max[i] + 1; + if ((min[i] <= max[i]) && + (min[i] > limit) && + (pixels[min[i] - 1 - x_offset] == foreground_colour)) { + min[i]--; + changed = TRUE; + } + + /* Extend Right by one pixel*/ + if (next == NULL || next->min[i] > next->max[i]) + limit = imbox.right () - 1;//-1 to index inside pix + else + limit = next->min[i] - 1; + if ((min[i] <= max[i]) && + (max[i] < limit) && + (pixels[max[i] + 1 - x_offset] == foreground_colour)) { + max[i]++; + changed = TRUE; + } + + /* Extend down by one row */ + if (pixels_below != NULL) { + if (min[i] < min[i - 1]) { //row goes left of row below + if (prev == NULL || prev->max[i - 1] < prev->min[i - 1]) + left_limit = min[i]; + else + left_limit = LARGEST (min[i], prev->max[i - 1] + 1); + } + else + left_limit = min[i - 1]; + + if (max[i] > max[i - 1]) { //row goes right of row below + if (next == NULL || next->min[i - 1] > next->max[i - 1]) + right_limit = max[i]; + else + right_limit = SMALLEST (max[i], next->min[i - 1] - 1); + } + else + right_limit = max[i - 1]; + + while ((left_limit <= right_limit) && + (pixels_below[left_limit - x_offset] != foreground_colour)) + left_limit++; //find black extremity + + if ((left_limit <= right_limit) && (left_limit < min[i - 1])) { + min[i - 1] = left_limit; //widen left if poss + changed = TRUE; + } + + while ((left_limit <= right_limit) && + (pixels_below[right_limit - x_offset] != foreground_colour)) + right_limit--; //find black extremity + + if ((left_limit <= right_limit) && (right_limit > max[i - 1])) { + max[i - 1] = right_limit;//widen right if poss + changed = TRUE; + } + } + + /* Extend up by one row */ + if (pixels_above != NULL) { + if (min[i] < min[i + 1]) { //row goes left of row above + if (prev == NULL || prev->min[i + 1] > prev->max[i + 1]) + left_limit = min[i]; + else + left_limit = LARGEST (min[i], prev->max[i + 1] + 1); + } + else + left_limit = min[i + 1]; + + if (max[i] > max[i + 1]) { //row goes right of row above + if (next == NULL || next->min[i + 1] > next->max[i + 1]) + right_limit = max[i]; + else + right_limit = SMALLEST (max[i], next->min[i + 1] - 1); + } + else + right_limit = max[i + 1]; + + while ((left_limit <= right_limit) && + (pixels_above[left_limit - x_offset] != foreground_colour)) + left_limit++; //find black extremity + + if ((left_limit <= right_limit) && (left_limit < min[i + 1])) { + min[i + 1] = left_limit; //widen left if poss + changed = TRUE; + } + + while ((left_limit <= right_limit) && + (pixels_above[right_limit - x_offset] != foreground_colour)) + right_limit--; //find black extremity + + if ((left_limit <= right_limit) && (right_limit > max[i + 1])) { + max[i + 1] = right_limit;//widen right if poss + changed = TRUE; + } + } + } + return changed; +} + + +/************************************************************************* + * PIXROW::char_clip_image() + * Cut out a sub image for a character + *************************************************************************/ + +void PIXROW::char_clip_image( //box of imlines extnt + IMAGELINE *imlines, + BOX &im_box, + ROW *row, //row containing word + IMAGE &clip_image, //unscaled sq subimage + float &baseline_pos //baseline ht in image + ) { + INT16 clip_image_xsize; //sub image x size + INT16 clip_image_ysize; //sub image y size + INT16 x_shift; //from pixrow to subim + INT16 y_shift; //from pixrow to subim + BOX char_pix_box; //bbox of char pixels + INT16 y_dest; + INT16 x_min; + INT16 x_max; + INT16 x_min_dest; + INT16 x_max_dest; + INT16 x_width; + INT16 y; + + clip_image_xsize = clip_image.get_xsize (); + clip_image_ysize = clip_image.get_ysize (); + + char_pix_box = bounding_box (); + /* + The y shift is calculated by first finding the coord of the bottom of the + image relative to the image lines. Then reducing this so by the amount + relative to the clip image size, necessary to vertically position the + character. + */ + y_shift = char_pix_box.bottom () - row_offset - + (INT16) floor ((clip_image_ysize - char_pix_box.height () + 0.5) / 2); + + /* + The x_shift is the shift to be applied to the page coord in the pixrow to + generate a centred char in the clip image. Thus the left hand edge of the + char is shifted to the margin width of the centred character. + */ + x_shift = char_pix_box.left () - + (INT16) floor ((clip_image_xsize - char_pix_box.width () + 0.5) / 2); + + for (y = 0; y < row_count; y++) { + /* + Check that there is something in this row of the source that will fit in the + sub image. If there is, reduce x range if necessary, then copy it + */ + y_dest = y - y_shift; + if ((min[y] <= max[y]) && (y_dest >= 0) && (y_dest < clip_image_ysize)) { + x_min = min[y]; + x_min_dest = x_min - x_shift; + if (x_min_dest < 0) { + x_min = x_min - x_min_dest; + x_min_dest = 0; + } + x_max = max[y]; + x_max_dest = x_max - x_shift; + if (x_max_dest > clip_image_xsize - 1) { + x_max = x_max - (x_max_dest - (clip_image_xsize - 1)); + x_max_dest = clip_image_xsize - 1; + } + x_width = x_max - x_min + 1; + if (x_width > 0) { + x_min -= im_box.left (); + //offset pixel ptr + imlines[y].pixels += x_min; + clip_image.put_line (x_min_dest, y_dest, x_width, imlines + y, + 0); + imlines[y].init (); //reset pixel ptr + } + } + } + /* + Baseline position relative to clip image: First find the baseline relative + to the page origin at the x coord of the centre of the character. Then + make this relative to the character bottom. Finally shift by the margin + between the bottom of the character and the bottom of the clip image. + */ + if (row == NULL) + baseline_pos = 0; //Not needed + else + baseline_pos = row->base_line ((char_pix_box.left () + + char_pix_box.right ()) / 2.0) + - char_pix_box.bottom () + + ((clip_image_ysize - char_pix_box.height ()) / 2); +} + + +/************************************************************************* + * char_clip_word() + * + * Generate a PIXROW_LIST with one element for each blob in the word, together + * with the image lines for the whole word. + *************************************************************************/ + +void char_clip_word( // + WERD *word, //word to be processed + IMAGE &bin_image, //whole image + PIXROW_LIST *&pixrow_list, //pixrows built + IMAGELINE *&imlines, //lines cut from image + BOX &pix_box //box defining imlines + ) { + BOX word_box = word->bounding_box (); + PBLOB_LIST *blob_list; + PBLOB_IT blob_it; + PIXROW_IT pixrow_it; + INT16 pix_offset; //Y pos of pixrow[0] + INT16 row_height; //No of pix rows + INT16 imlines_x_offset; + PIXROW *prev; + PIXROW *next; + PIXROW *current; + BOOL8 changed; //still improving + BOOL8 just_changed; //still improving + INT16 iteration_count = 0; + INT16 foreground_colour; + + if (word->flag (W_INVERSE)) + foreground_colour = 1; + else + foreground_colour = 0; + + /* Define region for max pixrow expansion */ + pix_box = word_box; + pix_box.move_bottom_edge (-pix_word_margin); + pix_box.move_top_edge (pix_word_margin); + pix_box.move_left_edge (-pix_word_margin); + pix_box.move_right_edge (pix_word_margin); + pix_box -= BOX (ICOORD (0, 0 + BUG_OFFSET), + ICOORD (bin_image.get_xsize (), + bin_image.get_ysize () - BUG_OFFSET)); + + /* Generate pixrows list */ + + pix_offset = pix_box.bottom (); + row_height = pix_box.height (); + blob_list = word->blob_list (); + blob_it.set_to_list (blob_list); + + pixrow_list = new PIXROW_LIST; + pixrow_it.set_to_list (pixrow_list); + + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + PIXROW *row = new PIXROW (pix_offset, row_height, blob_it.data ()); + ASSERT_HOST (!row-> + bad_box (bin_image.get_xsize (), bin_image.get_ysize ())); + pixrow_it.add_after_then_move (row); + } + + imlines = generate_imlines (bin_image, pix_box); + + /* Contract pixrows - shrink min and max back to black pixels */ + + imlines_x_offset = pix_box.left (); + + pixrow_it.move_to_first (); + for (pixrow_it.mark_cycle_pt (); + !pixrow_it.cycled_list (); pixrow_it.forward ()) { + ASSERT_HOST (!pixrow_it.data ()-> + bad_box (bin_image.get_xsize (), bin_image.get_ysize ())); + pixrow_it.data ()->contract (imlines, imlines_x_offset, + foreground_colour); + ASSERT_HOST (!pixrow_it.data ()-> + bad_box (bin_image.get_xsize (), bin_image.get_ysize ())); + } + + /* Expand pixrows iteratively 1 pixel at a time */ + do { + changed = FALSE; + pixrow_it.move_to_first (); + prev = NULL; + current = NULL; + next = pixrow_it.data (); + for (pixrow_it.mark_cycle_pt (); + !pixrow_it.cycled_list (); pixrow_it.forward ()) { + prev = current; + current = next; + if (pixrow_it.at_last ()) + next = NULL; + else + next = pixrow_it.data_relative (1); + just_changed = current->extend (imlines, pix_box, prev, next, + foreground_colour); + ASSERT_HOST (!current-> + bad_box (bin_image.get_xsize (), + bin_image.get_ysize ())); + changed = changed || just_changed; + } + iteration_count++; + } + while (changed); +} + + +/************************************************************************* + * generate_imlines() + * Get an array of IMAGELINES holding a portion of an image + *************************************************************************/ + +IMAGELINE *generate_imlines( //get some imagelines + IMAGE &bin_image, //from here + BOX &pix_box) { + IMAGELINE *imlines; //array of lines + int i; + + imlines = new IMAGELINE[pix_box.height ()]; + for (i = 0; i < pix_box.height (); i++) { + imlines[i].init (pix_box.width ()); + //coord to start at + bin_image.fast_get_line (pix_box.left (), + pix_box.bottom () + i + BUG_OFFSET, + //line to get + pix_box.width (), //width to get + imlines + i); //dest imline + } + return imlines; +} + + +/************************************************************************* + * display_clip_image() + * All the boring user interface bits to let you see what's going on + *************************************************************************/ + +#ifndef GRAPHICS_DISABLED +WINDOW display_clip_image(WERD *word, //word to be processed + IMAGE &bin_image, //whole image + PIXROW_LIST *pixrow_list, //pixrows built + BOX &pix_box //box of subimage + ) { + WINDOW clip_window; //window for debug + BOX word_box = word->bounding_box (); + int border = word_box.height () / 2; + BOX display_box = word_box; + + display_box.move_bottom_edge (-border); + display_box.move_top_edge (border); + display_box.move_left_edge (-border); + display_box.move_right_edge (border); + display_box -= BOX (ICOORD (0, 0 - BUG_OFFSET), + ICOORD (bin_image.get_xsize (), + bin_image.get_ysize () - BUG_OFFSET)); + + pgeditor_msg ("Creating Clip window..."); + clip_window = + create_window ("Clipped Blobs", + SCROLLINGWIN, + editor_word_xpos, editor_word_ypos, + 3 * (word_box.width () + 2 * border), + 3 * (word_box.height () + 2 * border), + //window width,height + // xmin, xmax + display_box.left (), display_box.right (), + display_box.bottom () - BUG_OFFSET, + display_box.top () - BUG_OFFSET, + // ymin, ymax + TRUE, FALSE, FALSE, TRUE); // down event & key only + pgeditor_msg ("Creating Clip window...Done"); + + clear_view_surface(clip_window); + show_sub_image (&bin_image, + display_box.left (), + display_box.bottom (), + display_box.width (), + display_box.height (), + clip_window, + display_box.left (), display_box.bottom () - BUG_OFFSET); + + word->plot (clip_window, RED); + word_box.plot (clip_window, INT_HOLLOW, TRUE, BLUE, BLUE); + pix_box.plot (clip_window, INT_HOLLOW, TRUE, BLUE, BLUE); + plot_pixrows(pixrow_list, clip_window); + overlap_picture_ops(TRUE); + return clip_window; +} + + +/************************************************************************* + * display_images() + * Show a pair of clip and scaled character images and wait for key before + * continuing. + *************************************************************************/ + +void display_images(IMAGE &clip_image, IMAGE &scaled_image) { + WINDOW clip_im_window; //window for debug + WINDOW scale_im_window; //window for debug + INT16 i; + GRAPHICS_EVENT event; // c; + + // xmin xmax ymin ymax + clip_im_window = create_window ("Clipped Blob", SCROLLINGWIN, editor_word_xpos - 20, editor_word_ypos - 100, 5 * clip_image.get_xsize (), 5 * clip_image.get_ysize (), 0, clip_image.get_xsize (), 0, clip_image.get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(clip_im_window); + show_sub_image (&clip_image, + 0, 0, + clip_image.get_xsize (), clip_image.get_ysize (), + clip_im_window, 0, 0); + + line_color_index(clip_im_window, RED); + for (i = 1; i < clip_image.get_xsize (); i++) { + move2d (clip_im_window, i, 0); + draw2d (clip_im_window, i, clip_image.get_xsize ()); + } + for (i = 1; i < clip_image.get_ysize (); i++) { + move2d (clip_im_window, 0, i); + draw2d (clip_im_window, clip_image.get_xsize (), i); + } + + // xmin xmax ymin ymax + scale_im_window = create_window ("Scaled Blob", SCROLLINGWIN, editor_word_xpos + 300, editor_word_ypos - 100, 5 * scaled_image.get_xsize (), 5 * scaled_image.get_ysize (), 0, scaled_image.get_xsize (), 0, scaled_image.get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(scale_im_window); + show_sub_image (&scaled_image, + 0, 0, + scaled_image.get_xsize (), scaled_image.get_ysize (), + scale_im_window, 0, 0); + + line_color_index(scale_im_window, RED); + for (i = 1; i < scaled_image.get_xsize (); i++) { + move2d (scale_im_window, i, 0); + draw2d (scale_im_window, i, scaled_image.get_xsize ()); + } + for (i = 1; i < scaled_image.get_ysize (); i++) { + move2d (scale_im_window, 0, i); + draw2d (scale_im_window, scaled_image.get_xsize (), i); + } + + overlap_picture_ops(TRUE); + await_event(scale_im_window, TRUE, ANY_EVENT, &event); + destroy_window(clip_im_window); + destroy_window(scale_im_window); +} + + +/************************************************************************* + * plot_pixrows() + * Display a list of pixrows + *************************************************************************/ + +void plot_pixrows( //plot for all blobs + PIXROW_LIST *pixrow_list, + WINDOW win) { + PIXROW_IT pixrow_it(pixrow_list); + INT16 colour = RED; + + for (pixrow_it.mark_cycle_pt (); + !pixrow_it.cycled_list (); pixrow_it.forward ()) { + if (colour > RED + 7) + colour = RED; + + perimeter_color_index (win, (COLOUR) colour); + interior_style(win, INT_HOLLOW, TRUE); + pixrow_it.data ()->plot (win); + colour++; + } +} +#endif diff --git a/ccmain/charcut.h b/ccmain/charcut.h new file mode 100644 index 0000000000..b447516232 --- /dev/null +++ b/ccmain/charcut.h @@ -0,0 +1,119 @@ +/********************************************************************** + * File: charcut.h (Formerly charclip.h) + * Description: Code for character clipping + * Author: Phil Cheatle + * Created: Wed Nov 11 08:35:15 GMT 1992 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef CHARCUT_H +#define CHARCUT_H + +#include "pgedit.h" +#include "notdll.h" +#include "notdll.h" + +/************************************************************************* + * CLASS PIXROW + * + * This class describes the pixels occupied by a blob. It uses two arrays, (min + * and max), each with one element per row, to identify the min and max x + * coordinates of the black pixels in the character on that row of the image. + * The number of rows used to describe the blob is held in row_count - note that + * some rows may be unoccupied - signified by max < min. The page coordinate of + * the row defined by min[0] and max[0] is held in row_offset. + *************************************************************************/ + +class PIXROW:public ELIST_LINK +{ + public: + INT16 row_offset; //y coord of min[0] + INT16 row_count; //length of arrays + INT16 *min; //array of min x + INT16 *max; //array of max x + + PIXROW() { //empty constructor + row_offset = 0; + row_count = 0; + min = NULL; + max = NULL; + } + PIXROW( //specified size + INT16 pos, + INT16 count, + PBLOB *blob); + + ~PIXROW () { //destructor + if (min != NULL) + free_mem(min); + if (max != NULL) + free_mem(max); + max = NULL; + } + + void plot( //use current settings + WINDOW fd) const; //where to paint + + BOX bounding_box() const; //return bounding box + //return true if box exceeds image + bool bad_box(int xsize, int ysize) const; + + void contract( //force end on black + IMAGELINE *imlines, //image array + INT16 x_offset, //of pixels[0] + INT16 foreground_colour); //0 or 1 + + //image array + BOOL8 extend(IMAGELINE *imlines, + BOX &imbox, + PIXROW *prev, //for prev blob + PIXROW *next, //for next blob + INT16 foreground_colour); //0 or 1 + + //box of imlines extnt + void char_clip_image(IMAGELINE *imlines, + BOX &im_box, + ROW *row, //row containing word + IMAGE &clip_image, //unscaled char image + float &baseline_pos); //baseline ht in image + +}; + +ELISTIZEH (PIXROW) +extern INT_VAR_H (pix_word_margin, 3, "How far outside word BB to grow"); +extern BOOL_VAR_H (show_char_clipping, TRUE, "Show clip image window?"); +extern INT_VAR_H (net_image_width, 40, "NN input image width"); +extern INT_VAR_H (net_image_height, 36, "NN input image height"); +extern INT_VAR_H (net_image_x_height, 22, "NN input image x_height"); +void char_clip_word( // + WERD *word, //word to be processed + IMAGE &bin_image, //whole image + PIXROW_LIST *&pixrow_list, //pixrows built + IMAGELINE *&imlines, //lines cut from image + BOX &pix_box //box defining imlines + ); +IMAGELINE *generate_imlines( //get some imagelines + IMAGE &bin_image, //from here + BOX &pix_box); + //word to be processed +WINDOW display_clip_image(WERD *word, + IMAGE &bin_image, //whole image + PIXROW_LIST *pixrow_list, //pixrows built + BOX &pix_box //box of subimage + ); +void display_images(IMAGE &clip_image, IMAGE &scaled_image); +void plot_pixrows( //plot for all blobs + PIXROW_LIST *pixrow_list, + WINDOW win); +#endif diff --git a/ccmain/charsample.cpp b/ccmain/charsample.cpp new file mode 100644 index 0000000000..84cd79075c --- /dev/null +++ b/ccmain/charsample.cpp @@ -0,0 +1,698 @@ +/********************************************************************** + * File: charsample.cpp (Formerly charsample.c) + * Description: Class to contain character samples and match scores + * to be used for adaption + * Author: Chris Newton + * Created: Thu Oct 7 13:40:37 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include +#include +#ifdef __UNIX__ +#include +#include +#endif +#include "memry.h" +#include "tessvars.h" +#include "statistc.h" +#include "charsample.h" +#include "paircmp.h" +#include "matmatch.h" +#include "adaptions.h" +#include "secname.h" +#include "notdll.h" + +extern INT32 demo_word; // Hack for demos + +ELISTIZE (CHAR_SAMPLE) ELISTIZE (CHAR_SAMPLES) CHAR_SAMPLE::CHAR_SAMPLE () { + sample_blob = NULL; + sample_denorm = NULL; + sample_image = NULL; + ch = '\0'; + n_samples_matched = 0; + total_match_scores = 0.0; + sumsq_match_scores = 0.0; +} + + +CHAR_SAMPLE::CHAR_SAMPLE(PBLOB *blob, DENORM *denorm, char c) { + sample_blob = blob; + sample_denorm = denorm; + sample_image = NULL; + ch = c; + n_samples_matched = 0; + total_match_scores = 0.0; + sumsq_match_scores = 0.0; +} + + +CHAR_SAMPLE::CHAR_SAMPLE(IMAGE *image, char c) { + sample_blob = NULL; + sample_denorm = NULL; + sample_image = image; + ch = c; + n_samples_matched = 0; + total_match_scores = 0.0; + sumsq_match_scores = 0.0; +} + + +float CHAR_SAMPLE::match_sample( // Update match scores + CHAR_SAMPLE *test_sample, + BOOL8 updating) { + float score1; + float score2; + IMAGE *image = test_sample->image (); + + if (sample_blob != NULL && test_sample->blob () != NULL) { + PBLOB *blob = test_sample->blob (); + DENORM *denorm = test_sample->denorm (); + + score1 = compare_bln_blobs (sample_blob, sample_denorm, blob, denorm); + score2 = compare_bln_blobs (blob, denorm, sample_blob, sample_denorm); + + score1 = (score1 > score2) ? score1 : score2; + } + else if (sample_image != NULL && image != NULL) { + CHAR_PROTO *sample = new CHAR_PROTO (this); + + score1 = matrix_match (sample_image, image); + delete sample; + } + else + return BAD_SCORE; + + if ((tessedit_use_best_sample || tessedit_cluster_debug) && updating) { + n_samples_matched++; + total_match_scores += score1; + sumsq_match_scores += score1 * score1; + } + return score1; +} + + +double CHAR_SAMPLE::mean_score() { + if (n_samples_matched > 0) + return (total_match_scores / n_samples_matched); + else + return BAD_SCORE; +} + + +double CHAR_SAMPLE::variance() { + double mean = mean_score (); + + if (n_samples_matched > 0) { + return (sumsq_match_scores / n_samples_matched) - mean * mean; + } + else + return BAD_SCORE; +} + + +void CHAR_SAMPLE::print(FILE *f) { + if (!tessedit_cluster_debug) + return; + + if (n_samples_matched > 0) + fprintf (f, + "%c - sample matched against " INT32FORMAT + " blobs, mean: %f, var: %f\n", ch, n_samples_matched, + mean_score (), variance ()); + else + fprintf (f, "No matches for this sample (%c)\n", ch); +} + + +void CHAR_SAMPLE::reset_match_statistics() { + n_samples_matched = 0; + total_match_scores = 0.0; + sumsq_match_scores = 0.0; +} + + +CHAR_SAMPLES::CHAR_SAMPLES() { + type = UNKNOWN; + samples.clear (); + ch = '\0'; + best_sample = NULL; + proto = NULL; +} + + +CHAR_SAMPLES::CHAR_SAMPLES(CHAR_SAMPLE *sample) { + CHAR_SAMPLE_IT sample_it = &samples; + + ASSERT_HOST (sample->image () != NULL || sample->blob () != NULL); + + if (sample->image () != NULL) + type = IMAGE_CLUSTER; + else if (sample->blob () != NULL) + type = BLOB_CLUSTER; + + samples.clear (); + sample_it.add_to_end (sample); + if (tessedit_mm_only_match_same_char) + ch = sample->character (); + else + ch = '\0'; + best_sample = NULL; + proto = NULL; +} + + +void CHAR_SAMPLES::add_sample(CHAR_SAMPLE *sample) { + CHAR_SAMPLE_IT sample_it = &samples; + + if (tessedit_use_best_sample || tessedit_cluster_debug) + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) { + sample_it.data ()->match_sample (sample, TRUE); + sample->match_sample (sample_it.data (), TRUE); + } + + sample_it.add_to_end (sample); + + if (tessedit_mm_use_prototypes && type == IMAGE_CLUSTER) + if (samples.length () == tessedit_mm_prototype_min_size) + this->build_prototype (); + else if (samples.length () > tessedit_mm_prototype_min_size) + this->add_sample_to_prototype (sample); +} + + +void CHAR_SAMPLES::add_sample_to_prototype(CHAR_SAMPLE *sample) { + BOOL8 rebuild = FALSE; + INT32 new_xsize = proto->x_size (); + INT32 new_ysize = proto->y_size (); + INT32 sample_xsize = sample->image ()->get_xsize (); + INT32 sample_ysize = sample->image ()->get_ysize (); + + if (sample_xsize > new_xsize) { + new_xsize = sample_xsize; + rebuild = TRUE; + } + if (sample_ysize > new_ysize) { + new_ysize = sample_ysize; + rebuild = TRUE; + } + + if (rebuild) + proto->enlarge_prototype (new_xsize, new_ysize); + + proto->add_sample (sample); +} + + +void CHAR_SAMPLES::build_prototype() { + CHAR_SAMPLE_IT sample_it = &samples; + CHAR_SAMPLE *sample; + INT32 proto_xsize = 0; + INT32 proto_ysize = 0; + + if (type != IMAGE_CLUSTER + || samples.length () < tessedit_mm_prototype_min_size) + return; + + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) { + sample = sample_it.data (); + if (sample->image ()->get_xsize () > proto_xsize) + proto_xsize = sample->image ()->get_xsize (); + if (sample->image ()->get_ysize () > proto_ysize) + proto_ysize = sample->image ()->get_ysize (); + } + + proto = new CHAR_PROTO (proto_xsize, proto_ysize, 0, 0, '\0'); + + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) + this->add_sample_to_prototype (sample_it.data ()); + +} + + +void CHAR_SAMPLES::find_best_sample() { + CHAR_SAMPLE_IT sample_it = &samples; + double score; + double best_score = MAX_INT32; + + if (ch == '\0' || samples.length () < tessedit_mm_prototype_min_size) + return; + + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) { + score = sample_it.data ()->mean_score (); + if (score < best_score) { + best_score = score; + best_sample = sample_it.data (); + } + } + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) { + tprintf ("Best sample for this %c cluster:\n", ch); + best_sample->print (debug_fp); + } + #endif +} + + +float CHAR_SAMPLES::match_score(CHAR_SAMPLE *sample) { + if (tessedit_mm_only_match_same_char && sample->character () != ch) + return BAD_SCORE; + + if (tessedit_use_best_sample && best_sample != NULL) + return best_sample->match_sample (sample, FALSE); + else if ((tessedit_mm_use_prototypes + || tessedit_mm_adapt_using_prototypes) && proto != NULL) + return proto->match_sample (sample); + else + return this->nn_match_score (sample); +} + + +float CHAR_SAMPLES::nn_match_score(CHAR_SAMPLE *sample) { + CHAR_SAMPLE_IT sample_it = &samples; + float score; + float min_score = MAX_INT32; + + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) { + score = sample_it.data ()->match_sample (sample, FALSE); + if (score < min_score) + min_score = score; + } + + return min_score; +} + + +void CHAR_SAMPLES::assign_to_char() { + STATS char_frequency(FIRST_CHAR, LAST_CHAR); + CHAR_SAMPLE_IT sample_it = &samples; + INT32 i; + INT32 max_index = 0; + INT32 max_freq = 0; + + if (samples.length () == 0 || tessedit_mm_only_match_same_char) + return; + + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) + char_frequency.add ((INT32) sample_it.data ()->character (), 1); + + for (i = FIRST_CHAR; i <= LAST_CHAR; i++) + if (char_frequency.pile_count (i) > max_freq) { + max_index = i; + max_freq = char_frequency.pile_count (i); + } + + if (samples.length () >= tessedit_cluster_min_size + && max_freq > samples.length () * tessedit_cluster_accept_fraction) + ch = (char) max_index; +} + + +void CHAR_SAMPLES::print(FILE *f) { + CHAR_SAMPLE_IT sample_it = &samples; + + fprintf (f, "Collected " INT32FORMAT " samples\n", samples.length ()); + + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) + sample_it.data ()->print (f); + + if (ch == '\0') + fprintf (f, "\nCluster not used for adaption\n"); + else + fprintf (f, "\nCluster used to adapt to '%c's\n", ch); + #endif +} + + +CHAR_PROTO::CHAR_PROTO() { + xsize = 0; + ysize = 0; + ch = '\0'; + nsamples = 0; + proto_data = NULL; + proto = NULL; +} + + +CHAR_PROTO::CHAR_PROTO(INT32 x_size, + INT32 y_size, + INT32 n_samples, + float initial_value, + char c) { + INT32 x; + INT32 y; + + xsize = x_size; + ysize = y_size; + ch = c; + nsamples = n_samples; + + ALLOC_2D_ARRAY(xsize, ysize, proto_data, proto, float); + + for (y = 0; y < ysize; y++) + for (x = 0; x < xsize; x++) + proto[x][y] = initial_value; +} + + +CHAR_PROTO::CHAR_PROTO(CHAR_SAMPLE *sample) { + INT32 x; + INT32 y; + IMAGELINE imline_s; + + if (sample->image () == NULL) { + xsize = 0; + ysize = 0; + ch = '\0'; + nsamples = 0; + proto_data = NULL; + proto = NULL; + } + else { + ch = sample->character (); + xsize = sample->image ()->get_xsize (); + ysize = sample->image ()->get_ysize (); + nsamples = 1; + + ALLOC_2D_ARRAY(xsize, ysize, proto_data, proto, float); + + for (y = 0; y < ysize; y++) { + sample->image ()->fast_get_line (0, y, xsize, &imline_s); + for (x = 0; x < xsize; x++) + if (imline_s.pixels[x] == BINIM_WHITE) + proto[x][y] = 1.0; + else + proto[x][y] = -1.0; + } + } +} + + +CHAR_PROTO::~CHAR_PROTO () { + if (proto_data != NULL) + FREE_2D_ARRAY(proto_data, proto); +} + + +float CHAR_PROTO::match_sample(CHAR_SAMPLE *test_sample) { + CHAR_PROTO *test_proto; + float score; + + if (test_sample->image () != NULL) { + test_proto = new CHAR_PROTO (test_sample); + if (xsize > test_proto->x_size ()) + score = this->match (test_proto); + else { + demo_word = -demo_word; // Flag different call + score = test_proto->match (this); + } + } + else + return BAD_SCORE; + + delete test_proto; + + return score; +} + + +float CHAR_PROTO::match(CHAR_PROTO *test_proto) { + INT32 xsize2 = test_proto->x_size (); + INT32 y_size; + INT32 y_size2; + INT32 x_offset; + INT32 y_offset; + INT32 x; + INT32 y; + CHAR_PROTO *match_proto; + float score; + float sum = 0.0; + + ASSERT_HOST (xsize >= xsize2); + + x_offset = (xsize - xsize2) / 2; + + if (ysize < test_proto->y_size ()) { + y_size = test_proto->y_size (); + y_size2 = ysize; + y_offset = (y_size - y_size2) / 2; + + match_proto = new CHAR_PROTO (xsize, + y_size, + nsamples * test_proto->n_samples (), + 0, '\0'); + + for (y = 0; y < y_offset; y++) { + for (x = 0; x < xsize2; x++) { + match_proto->data ()[x + x_offset][y] = + test_proto->data ()[x][y] * nsamples; + sum += match_proto->data ()[x + x_offset][y]; + } + } + + for (y = y_offset + y_size2; y < y_size; y++) { + for (x = 0; x < xsize2; x++) { + match_proto->data ()[x + x_offset][y] = + test_proto->data ()[x][y] * nsamples; + sum += match_proto->data ()[x + x_offset][y]; + } + } + + for (y = y_offset; y < y_offset + y_size2; y++) { + for (x = 0; x < x_offset; x++) { + match_proto->data ()[x][y] = proto[x][y - y_offset] * + test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (x = x_offset + xsize2; x < xsize; x++) { + match_proto->data ()[x][y] = proto[x][y - y_offset] * + test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (x = x_offset; x < x_offset + xsize2; x++) { + match_proto->data ()[x][y] = + proto[x][y - y_offset] * test_proto->data ()[x - x_offset][y]; + sum += match_proto->data ()[x][y]; + } + } + } + else { + y_size = ysize; + y_size2 = test_proto->y_size (); + y_offset = (y_size - y_size2) / 2; + + match_proto = new CHAR_PROTO (xsize, + y_size, + nsamples * test_proto->n_samples (), + 0, '\0'); + + for (y = 0; y < y_offset; y++) + for (x = 0; x < xsize; x++) { + match_proto->data ()[x][y] = + proto[x][y] * test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (y = y_offset + y_size2; y < y_size; y++) + for (x = 0; x < xsize; x++) { + match_proto->data ()[x][y] = + proto[x][y] * test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (y = y_offset; y < y_offset + y_size2; y++) { + for (x = 0; x < x_offset; x++) { + match_proto->data ()[x][y] = + proto[x][y] * test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (x = x_offset + xsize2; x < xsize; x++) { + match_proto->data ()[x][y] = + proto[x][y] * test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (x = x_offset; x < x_offset + xsize2; x++) { + match_proto->data ()[x][y] = proto[x][y] * + test_proto->data ()[x - x_offset][y - y_offset]; + sum += match_proto->data ()[x][y]; + } + } + } + + score = (1.0 - sum / + (xsize * y_size * nsamples * test_proto->n_samples ())); + + if (tessedit_mm_debug) { + if (score < 0) { + tprintf ("Match score %f\n", score); + tprintf ("x: %d, y: %d, ns: %d, nt: %d, dx %d, dy: %d\n", + xsize, y_size, nsamples, test_proto->n_samples (), + x_offset, y_offset); + for (y = 0; y < y_size; y++) { + tprintf ("\n%d", y); + for (x = 0; x < xsize; x++) + tprintf ("\t%d", match_proto->data ()[x][y]); + + } + tprintf ("\n"); + fflush(debug_fp); + } + } + +#ifndef GRAPHICS_DISABLED + if (tessedit_display_mm) { + tprintf ("Match score %f\n", score); + display_images (this->make_image (), + test_proto->make_image (), match_proto->make_image ()); + } + else if (demo_word != 0) { + if (demo_word > 0) + display_image (test_proto->make_image (), "Test sample", + 300, 400, FALSE); + else + display_image (this->make_image (), "Test sample", 300, 400, FALSE); + + display_image (match_proto->make_image (), "Best match", + 700, 400, TRUE); + } +#endif + + delete match_proto; + + return score; +} + + +void CHAR_PROTO::enlarge_prototype(INT32 new_xsize, INT32 new_ysize) { + float *old_proto_data = proto_data; + float **old_proto = proto; + INT32 old_xsize = xsize; + INT32 old_ysize = ysize; + INT32 x_offset; + INT32 y_offset; + INT32 x; + INT32 y; + + ASSERT_HOST (new_xsize >= xsize && new_ysize >= ysize); + + xsize = new_xsize; + ysize = new_ysize; + ALLOC_2D_ARRAY(xsize, ysize, proto_data, proto, float); + x_offset = (xsize - old_xsize) / 2; + y_offset = (ysize - old_ysize) / 2; + + for (y = 0; y < y_offset; y++) + for (x = 0; x < xsize; x++) + proto[x][y] = nsamples; + + for (y = y_offset + old_ysize; y < ysize; y++) + for (x = 0; x < xsize; x++) + proto[x][y] = nsamples; + + for (y = y_offset; y < y_offset + old_ysize; y++) { + for (x = 0; x < x_offset; x++) + proto[x][y] = nsamples; + + for (x = x_offset + old_xsize; x < xsize; x++) + proto[x][y] = nsamples; + + for (x = x_offset; x < x_offset + old_xsize; x++) + proto[x][y] = old_proto[x - x_offset][y - y_offset]; + } + + FREE_2D_ARRAY(old_proto_data, old_proto); +} + + +void CHAR_PROTO::add_sample(CHAR_SAMPLE *sample) { + INT32 x_offset; + INT32 y_offset; + INT32 x; + INT32 y; + IMAGELINE imline_s; + INT32 sample_xsize = sample->image ()->get_xsize (); + INT32 sample_ysize = sample->image ()->get_ysize (); + + x_offset = (xsize - sample_xsize) / 2; + y_offset = (ysize - sample_ysize) / 2; + + ASSERT_HOST (x_offset >= 0 && y_offset >= 0); + + for (y = 0; y < y_offset; y++) + for (x = 0; x < xsize; x++) + proto[x][y]++; // Treat pixels outside the + // range as white + for (y = y_offset + sample_ysize; y < ysize; y++) + for (x = 0; x < xsize; x++) + proto[x][y]++; + + for (y = y_offset; y < y_offset + sample_ysize; y++) { + sample->image ()->fast_get_line (0, + y - y_offset, sample_xsize, &imline_s); + for (x = x_offset; x < x_offset + sample_xsize; x++) { + if (imline_s.pixels[x - x_offset] == BINIM_WHITE) + proto[x][y]++; + else + proto[x][y]--; + } + + for (x = 0; x < x_offset; x++) + proto[x][y]++; + + for (x = x_offset + sample_xsize; x < xsize; x++) + proto[x][y]++; + } + + nsamples++; +} + + +IMAGE *CHAR_PROTO::make_image() { + IMAGE *image; + IMAGELINE imline_p; + INT32 x; + INT32 y; + + ASSERT_HOST (nsamples != 0); + + image = new (IMAGE); + image->create (xsize, ysize, 8); + + for (y = 0; y < ysize; y++) { + image->fast_get_line (0, y, xsize, &imline_p); + + for (x = 0; x < xsize; x++) { + imline_p.pixels[x] = 128 + + (UINT8) ((proto[x][y] * 128.0) / (0.00001 + nsamples)); + } + + image->fast_put_line (0, y, xsize, &imline_p); + } + return image; +} diff --git a/ccmain/control.cpp b/ccmain/control.cpp new file mode 100644 index 0000000000..663a2e9fb1 --- /dev/null +++ b/ccmain/control.cpp @@ -0,0 +1,1668 @@ +/****************************************************************** + * File: control.cpp (Formerly control.c) + * Description: Module-independent matcher controller. + * Author: Ray Smith + * Created: Thu Apr 23 11:09:58 BST 1992 + * ReHacked: Tue Sep 22 08:42:49 BST 1992 Phil Cheatle + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "mainblk.h" +#include +#include +#ifdef __UNIX__ +#include +#include +#include +#endif +#include +#include "ocrclass.h" +#include "werdit.h" +#include "drawfx.h" +#include "tfacep.h" +#include "tessbox.h" +#include "tessvars.h" +//#include "fxtop.h" +#include "pgedit.h" +#include "reject.h" +#include "adaptions.h" +#include "charcut.h" +#include "fixxht.h" +#include "fixspace.h" +#include "genblob.h" +#include "docqual.h" +#include "control.h" +#include "secname.h" +#include "output.h" +#include "callcpp.h" +#include "notdll.h" +#include "tordvars.h" +#include "adaptmatch.h" + +#define MIN_FONT_ROW_COUNT 8 +#define MAX_XHEIGHT_DIFF 3 + +#define EXTERN +//extern "C" { +//EXTERN BOOL_VAR(tessedit_small_match,FALSE,"Use small matrix matcher"); + +//extern FILE* matcher_fp; +//extern FILE* correct_fp; +//}; +BOOL_VAR (tessedit_small_match, FALSE, "Use small matrix matcher"); +EXTERN BOOL_VAR (tessedit_print_text, FALSE, "Write text to stdout"); +EXTERN BOOL_VAR (tessedit_draw_words, FALSE, "Draw source words"); +EXTERN BOOL_VAR (tessedit_draw_outwords, FALSE, "Draw output words"); +EXTERN BOOL_VAR (tessedit_training_wiseowl, FALSE, "Call WO to learn blobs"); +EXTERN BOOL_VAR (tessedit_training_tess, FALSE, "Call Tess to learn blobs"); +EXTERN BOOL_VAR (tessedit_matcher_is_wiseowl, FALSE, "Call WO to classify"); +EXTERN BOOL_VAR (tessedit_dump_choices, FALSE, "Dump char choices"); +EXTERN BOOL_VAR (tessedit_fix_fuzzy_spaces, TRUE, +"Try to improve fuzzy spaces"); +EXTERN BOOL_VAR (tessedit_unrej_any_wd, FALSE, +"Dont bother with word plausibility"); +EXTERN BOOL_VAR (tessedit_fix_hyphens, TRUE, "Crunch double hyphens?"); + +EXTERN BOOL_VAR (tessedit_reject_fullstops, FALSE, "Reject all fullstops"); +EXTERN BOOL_VAR (tessedit_reject_suspect_fullstops, FALSE, +"Reject suspect fullstops"); +EXTERN BOOL_VAR (tessedit_redo_xheight, TRUE, "Check/Correct x-height"); +EXTERN BOOL_VAR (tessedit_cluster_adaption_on, TRUE, +"Do our own adaption - ems only"); +EXTERN BOOL_VAR (tessedit_enable_doc_dict, TRUE, +"Add words to the document dictionary"); +EXTERN BOOL_VAR (word_occ_first, FALSE, "Do word occ before re-est xht"); +EXTERN BOOL_VAR (tessedit_debug_fonts, FALSE, "Output font info per char"); +EXTERN BOOL_VAR (tessedit_xht_fiddles_on_done_wds, TRUE, +"Apply xht fix up even if done"); +EXTERN BOOL_VAR (tessedit_xht_fiddles_on_no_rej_wds, TRUE, +"Apply xht fix up even in no rejects"); +EXTERN INT_VAR (x_ht_check_word_occ, 2, "Check Char Block occupancy"); +EXTERN INT_VAR (x_ht_stringency, 1, "How many confirmed a/n to accept?"); +EXTERN BOOL_VAR (x_ht_quality_check, TRUE, "Dont allow worse quality"); +EXTERN BOOL_VAR (tessedit_debug_block_rejection, FALSE, +"Block and Row stats"); +EXTERN INT_VAR (debug_x_ht_level, 0, "Reestimate debug"); +EXTERN BOOL_VAR (rej_use_xht, TRUE, "Individual rejection control"); +EXTERN BOOL_VAR (debug_acceptable_wds, FALSE, "Dump word pass/fail chk"); + +EXTERN STRING_VAR (chs_leading_punct, "('`\"", "Leading punctuation"); +EXTERN +STRING_VAR (chs_trailing_punct1, ").,;:?!", "1st Trailing punctuation"); +EXTERN STRING_VAR (chs_trailing_punct2, ")'`\"", +"2nd Trailing punctuation"); + +EXTERN double_VAR (quality_rej_pc, 0.08, +"good_quality_doc lte rejection limit"); +EXTERN double_VAR (quality_blob_pc, 0.0, +"good_quality_doc gte good blobs limit"); +EXTERN double_VAR (quality_outline_pc, 1.0, +"good_quality_doc lte outline error limit"); +EXTERN double_VAR (quality_char_pc, 0.95, +"good_quality_doc gte good char limit"); +EXTERN INT_VAR (quality_min_initial_alphas_reqd, 2, +"alphas in a good word"); + +EXTERN BOOL_VAR (tessedit_tess_adapt_to_rejmap, FALSE, +"Use reject map to control Tesseract adaption"); +EXTERN INT_VAR (tessedit_tess_adaption_mode, 0x27, +"Adaptation decision algorithm for tess"); +EXTERN INT_VAR (tessedit_em_adaption_mode, 0, +"Adaptation decision algorithm for ems matrix matcher"); +EXTERN BOOL_VAR (tessedit_cluster_adapt_after_pass1, FALSE, +"Adapt using clusterer after pass 1"); +EXTERN BOOL_VAR (tessedit_cluster_adapt_after_pass2, FALSE, +"Adapt using clusterer after pass 1"); +EXTERN BOOL_VAR (tessedit_cluster_adapt_after_pass3, FALSE, +"Adapt using clusterer after pass 1"); +EXTERN BOOL_VAR (tessedit_cluster_adapt_before_pass1, FALSE, +"Adapt using clusterer before Tess adaping during pass 1"); +EXTERN INT_VAR (tessedit_cluster_adaption_mode, 0, +"Adaptation decision algorithm for matrix matcher"); +EXTERN BOOL_VAR (tessedit_adaption_debug, FALSE, +"Generate and print debug information for adaption"); +EXTERN BOOL_VAR (tessedit_minimal_rej_pass1, FALSE, +"Do minimal rejection on pass 1 output"); +EXTERN BOOL_VAR (tessedit_test_adaption, FALSE, +"Test adaption criteria"); +EXTERN BOOL_VAR (tessedit_global_adaption, FALSE, +"Adapt to all docs over time"); +EXTERN BOOL_VAR (tessedit_matcher_log, FALSE, "Log matcher activity"); +EXTERN INT_VAR (tessedit_test_adaption_mode, 3, +"Adaptation decision algorithm for tess"); + +EXTERN BOOL_VAR (test_pt, FALSE, "Test for point"); +EXTERN double_VAR (test_pt_x, 99999.99, "xcoord"); +EXTERN double_VAR (test_pt_y, 99999.99, "ycoord"); + +extern int MatcherDebugLevel; +extern int display_ratings; +extern int number_debug; +extern int adjust_debug; +/* +extern "C" { + extern int MatcherDebugLevel; + extern int display_ratings; + extern int number_debug; + extern int adjust_debug; +// extern int LearningDebugLevel; + }; +*/ +FILE *choice_file = NULL; //Choice file ptr + +CLISTIZEH (PBLOB) CLISTIZE (PBLOB) +/* DEBUGGING */ +INT16 blob_count(WERD *w) { + return w->blob_list ()->length (); +} + + +/********************************************************************** + * recog_pseudo_word + * + * Make a word from the selected blobs and run Tess on them. + **********************************************************************/ + +void recog_pseudo_word( //recognize blobs + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box) { + WERD *word; + ROW *pseudo_row; //row of word + BLOCK *pseudo_block; //block of word + + word = make_pseudo_word (block_list, selection_box, + pseudo_block, pseudo_row); + if (word != NULL) { + recog_interactive(pseudo_block, pseudo_row, word); + delete word; + } +} + + +/********************************************************************** + * recog_interactive + * + * Recognize a single word in interactive mode. + **********************************************************************/ + +BOOL8 recog_interactive( //recognize blobs + BLOCK *, //block + ROW *row, //row of word + WERD *word //word to recognize + ) { + WERD_RES word_res(word); + INT16 char_qual; + INT16 good_char_qual; + + classify_word_pass2(&word_res, row); + #ifndef SECURE_NAMES + if (tessedit_debug_quality_metrics) { + word_char_quality(&word_res, row, &char_qual, &good_char_qual); + tprintf + ("\n%d chars; word_blob_quality: %d; outline_errs: %d; char_quality: %d; good_char_quality: %d\n", + word_res.reject_map.length (), word_blob_quality (&word_res, row), + word_outline_errs (&word_res), char_qual, good_char_qual); + } + #endif + return TRUE; +} + + +/********************************************************************** + * recog_all_words() + * + * Walk the current block list applying the specified word processor function + * to all words + **********************************************************************/ + +void recog_all_words( //process words + PAGE_RES *page_res, //page structure + volatile ETEXT_DESC *monitor //progress monitor + ) { + //reset page iterator + PAGE_RES_IT page_res_it(page_res); + INT16 chars_in_word; + INT16 rejects_in_word; + CHAR_SAMPLES_LIST em_clusters; + CHAR_SAMPLE_LIST ems_waiting; + CHAR_SAMPLES_LIST char_clusters; + CHAR_SAMPLE_LIST chars_waiting; + INT16 blob_quality = 0; + INT16 outline_errs = 0; + INT16 doc_blob_quality = 0; + INT16 doc_outline_errs = 0; + INT16 doc_char_quality = 0; + INT16 all_char_quality; + INT16 accepted_all_char_quality; + INT16 good_char_count = 0; + INT16 doc_good_char_quality = 0; + const STRING *wordstr; + const char *text; + int i; + + BOOL8 good_quality_doc; + UINT8 permuter_type; + + INT32 tess_adapt_mode = 0; + INT32 word_count; //count of words in doc + INT32 word_index; //current word + + if (tessedit_minimal_rej_pass1) { + tessedit_test_adaption.set_value (TRUE); + tessedit_minimal_rejection.set_value (TRUE); + } + + if (tessedit_cluster_adapt_before_pass1) { + tess_adapt_mode = tessedit_tess_adaption_mode; + tessedit_tess_adaption_mode.set_value (0); + tessedit_tess_adapt_to_rejmap.set_value (TRUE); + } + + /* Pass 1 */ + word_count = 0; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + while (page_res_it.word () != NULL) { + word_count++; + page_res_it.forward (); + } + page_res_it.restart_page (); + } + else + word_count = 1; + + word_index = 0; + int dict_words = 0; + while (page_res_it.word () != NULL) { + set_global_loc_code(LOC_PASS1); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 30 + 50 * word_index / word_count; + if ((monitor->end_time != 0 && clock() > monitor->end_time) || + (monitor->cancel != NULL && (*monitor->cancel)(monitor->cancel_this, + dict_words))) + return; + } + classify_word_pass1 (page_res_it.word (), + page_res_it.row ()->row, FALSE, NULL, NULL); + + if (tessedit_test_adaption && !tessedit_minimal_rejection) { + if (!word_adaptable (page_res_it.word (), + tessedit_test_adaption_mode)) + page_res_it.word ()->reject_map.rej_word_tess_failure (); + //FAKE PERM REJ + else { + wordstr = &(page_res_it.word ()->best_choice->string ()); + /* Override rejection mechanisms for this word */ + text = wordstr->string (); + for (i = 0; text[i] != '\0'; i++) { + if ((text[i] != ' ') + && page_res_it.word ()->reject_map[i].rejected ()) + page_res_it.word ()->reject_map[i]. + setrej_minimal_rej_accept(); + } + } + } + + if ((tessedit_cluster_adapt_after_pass1 + || tessedit_cluster_adapt_after_pass3 + || tessedit_cluster_adapt_before_pass1) + && tessedit_cluster_adaption_mode != 0) { + collect_characters_for_adaption (page_res_it.word (), + &char_clusters, &chars_waiting); + } + // Count dict words. + if (page_res_it.word()->best_choice->permuter() == USER_DAWG_PERM) + ++dict_words; + page_res_it.forward (); + } + + if (tessedit_cluster_adapt_before_pass1) + tessedit_tess_adaption_mode.set_value (tess_adapt_mode); + + page_res_it.restart_page (); + while ((tessedit_cluster_adapt_after_pass1 + || tessedit_cluster_adapt_before_pass1) + && page_res_it.word () != NULL) { + if (monitor != NULL) + monitor->ocr_alive = TRUE; + if (tessedit_cluster_adapt_after_pass1) + adapt_to_good_samples (page_res_it.word (), + &char_clusters, &chars_waiting); + else + classify_word_pass1 (page_res_it.word (), + page_res_it.row ()->row, + TRUE, &char_clusters, &chars_waiting); + + page_res_it.forward (); + } + + /* Pass 2 */ + page_res_it.restart_page (); + word_index = 0; + while (!tessedit_test_adaption && page_res_it.word () != NULL) { + set_global_loc_code(LOC_PASS2); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 80 + 10 * word_index / word_count; + if ((monitor->end_time != 0 && clock() > monitor->end_time) || + (monitor->cancel != NULL && (*monitor->cancel)(monitor->cancel_this, + dict_words))) + return; + } + classify_word_pass2 (page_res_it.word (), page_res_it.row ()->row); + + if (tessedit_em_adaption_mode > 0) + collect_ems_for_adaption (page_res_it.word (), + &em_clusters, &ems_waiting); + + if (tessedit_cluster_adapt_after_pass2 + && tessedit_cluster_adaption_mode != 0) + collect_characters_for_adaption (page_res_it.word (), + &char_clusters, &chars_waiting); + page_res_it.forward (); + } + + /* Another pass */ + set_global_loc_code(LOC_FUZZY_SPACE); + + if (!tessedit_test_adaption && tessedit_fix_fuzzy_spaces + && !tessedit_word_for_word) + fix_fuzzy_spaces(monitor, word_count, page_res); + + if (!tessedit_test_adaption && tessedit_em_adaption_mode != 0) + // Initially ems only + print_em_stats(&em_clusters, &ems_waiting); + + /* Pass 3 - used for checking confusion sets */ + page_res_it.restart_page (); + word_index = 0; + while (!tessedit_test_adaption && page_res_it.word () != NULL) { + set_global_loc_code(LOC_MM_ADAPT); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 95 + 5 * word_index / word_count; + } + check_debug_pt (page_res_it.word (), 70); + /* Use good matches to sort out confusions */ + + if (tessedit_em_adaption_mode != 0) + adapt_to_good_ems (page_res_it.word (), &em_clusters, &ems_waiting); + + if (tessedit_cluster_adapt_after_pass2 + && tessedit_cluster_adaption_mode != 0) + adapt_to_good_samples (page_res_it.word (), + &char_clusters, &chars_waiting); + + if (tessedit_reject_fullstops + && strchr (page_res_it.word ()->best_choice->string ().string (), + '.') != NULL) + reject_all_fullstops (page_res_it.word ()); + else if (tessedit_reject_suspect_fullstops + && strchr (page_res_it.word ()->best_choice->string (). + string (), '.') != NULL) + reject_suspect_fullstops (page_res_it.word ()); + + page_res_it.rej_stat_word (); + chars_in_word = page_res_it.word ()->reject_map.length (); + rejects_in_word = page_res_it.word ()->reject_map.reject_count (); + + blob_quality = word_blob_quality (page_res_it.word (), + page_res_it.row ()->row); + doc_blob_quality += blob_quality; + outline_errs = word_outline_errs (page_res_it.word ()); + doc_outline_errs += outline_errs; + word_char_quality (page_res_it.word (), + page_res_it.row ()->row, + &all_char_quality, &accepted_all_char_quality); + doc_char_quality += all_char_quality; + permuter_type = page_res_it.word ()->best_choice->permuter (); + if ((permuter_type == SYSTEM_DAWG_PERM) || + (permuter_type == FREQ_DAWG_PERM) || + (permuter_type == USER_DAWG_PERM)) { + good_char_count += chars_in_word - rejects_in_word; + doc_good_char_quality += accepted_all_char_quality; + } + check_debug_pt (page_res_it.word (), 80); + if (tessedit_reject_bad_qual_wds && + (blob_quality == 0) && (outline_errs >= chars_in_word)) + page_res_it.word ()->reject_map.rej_word_bad_quality (); + check_debug_pt (page_res_it.word (), 90); + page_res_it.forward (); + } + + page_res_it.restart_page (); + while (!tessedit_test_adaption + && tessedit_cluster_adapt_after_pass3 && page_res_it.word () != NULL) { + if (monitor != NULL) + monitor->ocr_alive = TRUE; + if (tessedit_cluster_adaption_mode != 0) + adapt_to_good_samples (page_res_it.word (), + &char_clusters, &chars_waiting); + page_res_it.forward (); + } + + #ifndef SECURE_NAMES + if (tessedit_debug_quality_metrics) { + tprintf + ("QUALITY: num_chs= %d num_rejs= %d %5.3f blob_qual= %d %5.3f outline_errs= %d %5.3f char_qual= %d %5.3f good_ch_qual= %d %5.3f\n", + page_res->char_count, page_res->rej_count, + page_res->rej_count / (float) page_res->char_count, doc_blob_quality, + doc_blob_quality / (float) page_res->char_count, doc_outline_errs, + doc_outline_errs / (float) page_res->char_count, doc_char_quality, + doc_char_quality / (float) page_res->char_count, + doc_good_char_quality, + good_char_count > + 0 ? doc_good_char_quality / (float) good_char_count : 0.0); + } + #endif + good_quality_doc = + (page_res->rej_count / (float) page_res->char_count <= quality_rej_pc) + && + (doc_blob_quality / (float) page_res->char_count >= quality_blob_pc) && + (doc_outline_errs / (float) page_res->char_count <= quality_outline_pc) && + (doc_char_quality / (float) page_res->char_count >= quality_char_pc); + + /* Do whole document or whole block rejection pass*/ + + if (!tessedit_test_adaption) { + set_global_loc_code(LOC_DOC_BLK_REJ); + quality_based_rejection(page_res_it, good_quality_doc); + } + font_recognition_pass(page_res_it); + + /* Write results pass */ + set_global_loc_code(LOC_WRITE_RESULTS); + // This is now redundant, but retained commented so show how to obtain + // bounding boxes and style information. + // output_pass (page_res_it, false); +} + + +/********************************************************************** + * classify_word_pass1 + * + * Baseline normalize the word and pass it to Tess. + **********************************************************************/ + +void classify_word_pass1( //recog one word + WERD_RES *word, //word to do + ROW *row, + BOOL8 cluster_adapt, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + WERD *bln_word; //baseline norm copy + //detailed results + BLOB_CHOICE_LIST_CLIST blob_choices; + BOOL8 adapt_ok; + const char *rejmap; + INT16 index; + STRING mapstr = ""; + char *match_string; + char word_string[1024]; + + if (matcher_fp != NULL) { + fgets (word_string, 1023, correct_fp); + if ((match_string = strchr (word_string, '\r')) != NULL) + *match_string = '\0'; + if ((match_string = strchr (word_string, '\n')) != NULL) + *match_string = '\0'; + if (word_string[0] != '\0') { + word->word->set_text (word_string); + word_answer = (char *) word->word->text (); + } + else + word_answer = NULL; + } + + check_debug_pt (word, 0); + matcher_pass = 0; + bln_word = make_bln_copy (word->word, row, row->x_height (), &word->denorm); + + word->best_choice = tess_segment_pass1 (bln_word, &word->denorm, + tess_default_matcher, + word->raw_choice, &blob_choices, + word->outword); + + /* + Test for TESS screw up on word. Recog_word has already ensured that the + choice list, outword blob lists and best_choice string are the same + length. A TESS screw up is indicated by a blank filled or 0 length string. + */ + if ((word->best_choice->string ().length () == 0) || + (strspn (word->best_choice->string ().string (), " ") == + word->best_choice->string ().length ())) { + word->done = FALSE; //Try again on pass2 - adaption may help + word->tess_failed = TRUE; + word->reject_map.initialise (word->best_choice->string ().length ()); + word->reject_map.rej_word_tess_failure (); + } + else { + word->tess_failed = FALSE; + if ((word->best_choice->string ().length () != + word->outword->blob_list ()->length ()) || + (word->best_choice->string ().length () != blob_choices.length ())) { + tprintf + ("ASSERT FAIL String:\"%s\"; Strlen=%d; #Blobs=%d; #Choices=%d\n", + word->best_choice->string ().string (), + word->best_choice->string ().length (), + word->outword->blob_list ()->length (), blob_choices.length ()); + } + ASSERT_HOST (word->best_choice->string ().length () == + word->outword->blob_list ()->length ()); + ASSERT_HOST (word->best_choice->string ().length () == + blob_choices.length ()); + + /* + The adaption step used to be here. It has been moved to after + make_reject_map so that we know whether the word will be accepted in the + first pass or not. This move will PREVENT adaption to words containing + double quotes because the word will not be identical to what tess thinks + its best choice is. (See CurrentBestChoiceIs in + danj/microfeatures/stopper.c which is used by AdaptableWord in + danj/microfeatures/adaptmatch.c) + */ + + if (word->word->flag (W_REP_CHAR)) { + fix_rep_char(word); + } + else { + fix_quotes ((char *) word->best_choice->string ().string (), + //turn to double + word->outword, &blob_choices); + if (tessedit_fix_hyphens) + //turn 2 to 1 + fix_hyphens ((char *) word->best_choice->string ().string (), word->outword, &blob_choices); + record_certainty (word->best_choice->certainty (), 1); + //accounting + + word->tess_accepted = tess_acceptable_word (word->best_choice, + word->raw_choice); + + word->tess_would_adapt = tess_adaptable_word (word->outword, + word->best_choice, + word->raw_choice); + // Also sets word->done flag + make_reject_map (word, &blob_choices, row, 1); + + adapt_ok = word_adaptable (word, tessedit_tess_adaption_mode); + + if (cluster_adapt) + adapt_to_good_samples(word, char_clusters, chars_waiting); + + if (adapt_ok || tessedit_tess_adapt_to_rejmap) { + if (!tessedit_tess_adapt_to_rejmap) + rejmap = NULL; + else { + ASSERT_HOST (word->reject_map.length () == + word->best_choice->string ().length ()); + + for (index = 0; index < word->reject_map.length (); index++) { + if (adapt_ok || word->reject_map[index].accepted ()) + mapstr += '1'; + else + mapstr += '0'; + } + rejmap = mapstr.string (); + } + + //adapt to it + tess_adapter (word->outword, &word->denorm, word->best_choice->string ().string (), word->raw_choice->string ().string (), rejmap); + } + + if (tessedit_enable_doc_dict) + tess_add_doc_word (word->best_choice); + set_word_fonts(word, &blob_choices); + } + } + if (tessedit_print_text) { + write_cooked_text (bln_word, word->best_choice->string (), + word->done, FALSE, stdout); + } + delete bln_word; + blob_choices.deep_clear (); +} + + +/********************************************************************** + * classify_word_pass2 + * + * Control what to do with the word in pass 2 + **********************************************************************/ + +void classify_word_pass2( //word to do + WERD_RES *word, + ROW *row) { + BOOL8 done_this_pass = FALSE; + WERD_RES new_x_ht_word (word->word); + float new_x_ht = 0.0; + INT16 old_xht_reject_count; + INT16 new_xht_reject_count; + INT16 old_xht_accept_count; + INT16 new_xht_accept_count; + BOOL8 accept_new_x_ht = FALSE; + INT16 old_chs_in_wd; + INT16 new_chs_in_wd; + INT16 old_word_quality; + INT16 new_word_quality; + INT16 dummy; + + set_global_subloc_code(SUBLOC_NORM); + check_debug_pt (word, 30); + if (!word->done || + tessedit_training_tess || + tessedit_training_wiseowl || tessedit_dump_choices) { + word->x_height = row->x_height (); + word->caps_height = 0.0; + if (word->outword != NULL) { + delete word->outword; //get rid of junk + delete word->best_choice; + delete word->raw_choice; + } + match_word_pass2 (word, row, row->x_height ()); + done_this_pass = TRUE; + check_debug_pt (word, 40); + } + + if (!word->tess_failed && !word->word->flag (W_REP_CHAR)) { + set_global_subloc_code(SUBLOC_FIX_XHT); + if ((tessedit_xht_fiddles_on_done_wds || !word->done) && + (tessedit_xht_fiddles_on_no_rej_wds || + (word->reject_map.reject_count () > 0))) { + if ((x_ht_check_word_occ >= 2) && word_occ_first) + check_block_occ(word); + + if (tessedit_redo_xheight) + re_estimate_x_ht(word, &new_x_ht); + + if (((x_ht_check_word_occ >= 2) && !word_occ_first) || + ((x_ht_check_word_occ >= 1) && (new_x_ht > 0))) + check_block_occ(word); + } + if (new_x_ht > 0) { + old_chs_in_wd = word->reject_map.length (); + + /* Re-estimated x_ht error suggests a rematch is worthwhile. */ + new_x_ht_word.x_height = new_x_ht; + new_x_ht_word.caps_height = 0.0; + match_word_pass2 (&new_x_ht_word, row, new_x_ht_word.x_height); + if (!new_x_ht_word.tess_failed) { + if ((x_ht_check_word_occ >= 1) && word_occ_first) + check_block_occ(&new_x_ht_word); + + re_estimate_x_ht(&new_x_ht_word, &new_x_ht); + + if ((x_ht_check_word_occ >= 1) && !word_occ_first) + check_block_occ(&new_x_ht_word); + + old_xht_reject_count = word->reject_map.reject_count (); + old_xht_accept_count = old_chs_in_wd - old_xht_reject_count; + new_xht_reject_count = new_x_ht_word.reject_map.reject_count (); + new_chs_in_wd = new_x_ht_word.reject_map.length (); + new_xht_accept_count = new_chs_in_wd - new_xht_reject_count; + accept_new_x_ht = + ((new_xht_accept_count > old_xht_accept_count) || + ((new_xht_accept_count == old_xht_accept_count) && + (new_xht_accept_count > 0))) && + (!new_x_ht_word.guessed_x_ht || + !new_x_ht_word.guessed_caps_ht); + + if (accept_new_x_ht && x_ht_quality_check) { + word_char_quality(word, row, &old_word_quality, &dummy); + word_char_quality(&new_x_ht_word, row, &new_word_quality, &dummy); + if (old_word_quality > new_word_quality) + accept_new_x_ht = FALSE; + } + + if (accept_new_x_ht && (x_ht_stringency > 0)) { + accept_new_x_ht = + (count_alphanums (&new_x_ht_word) > x_ht_stringency); + if (!accept_new_x_ht && rej_use_xht) { + if (debug_x_ht_level >= 1) + tprintf + ("Failed stringency test so reject original word\n"); + word->reject_map.rej_word_xht_fixup (); + } + } + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 1) { + tprintf ("New XHT Match:: %s ", + word->best_choice->string ().string ()); + word->reject_map.print (debug_fp); + tprintf (" -> %s ", + new_x_ht_word.best_choice->string ().string ()); + new_x_ht_word.reject_map.print (debug_fp); + tprintf (" %s->%s %s %s\n", + word->guessed_x_ht ? "GUESS" : "CERT", + new_x_ht_word.guessed_x_ht ? "GUESS" : "CERT", + new_x_ht > 0.1 ? "STILL DOUBT" : "OK", + accept_new_x_ht ? "ACCEPTED" : ""); + } + #endif + } + if (accept_new_x_ht) { + /* + The new x_ht is deemed superior so put the final results in the real word + and destroy the old results + */ + delete word->outword; //get rid of junk + word->outword = new_x_ht_word.outword; + word->denorm = new_x_ht_word.denorm; + delete word->best_choice; + word->best_choice = new_x_ht_word.best_choice; + delete word->raw_choice; + word->raw_choice = new_x_ht_word.raw_choice; + word->reject_map = new_x_ht_word.reject_map; + word->done = new_x_ht_word.done; + done_this_pass = TRUE; + } + else { + /* + The new x_ht is no better, so destroy the copy word and put any uncertain + x or cap ht estimate back to default. (I.e. dont blame me if its bad!) + Conditionally, use any ammended block occ chars. + */ + //get rid of junk + delete new_x_ht_word.outword; + delete new_x_ht_word.best_choice; + delete new_x_ht_word.raw_choice; + } + //to keep new destructor happy + new_x_ht_word.outword = NULL; + //to keep new destructor happy + new_x_ht_word.best_choice = NULL; + //to keep new destructor happy + new_x_ht_word.raw_choice = NULL; + + if (rej_mostly_reject_mode == 2) { + reject_mostly_rejects(word); + tprintf ("Rejecting mostly rejects on %s ", + word->best_choice->string ().string ()); + } + } + + set_global_subloc_code(SUBLOC_NORM); + + if (done_this_pass && !word->done && tessedit_save_stats) + SaveBadWord (word->best_choice->string ().string (), + word->best_choice->certainty ()); + record_certainty (word->best_choice->certainty (), 2); + //accounting + } +#ifndef GRAPHICS_DISABLED + if (tessedit_draw_outwords) { + if (fx_win == NO_WINDOW) + create_fx_win(); + clear_fx_win(); + word->outword->plot (fx_win); + make_picture_current(fx_win); + } +#endif + + set_global_subloc_code(SUBLOC_NORM); + if (tessedit_print_text) { + write_cooked_text (word->outword, word->best_choice->string (), + word->done, done_this_pass, stdout); + } + check_debug_pt (word, 50); +} + + +/********************************************************************** + * match_word_pass2 + * + * Baseline normalize the word and pass it to Tess. + **********************************************************************/ + +void match_word_pass2( //recog one word + WERD_RES *word, //word to do + ROW *row, + float x_height) { + WERD *bln_word; //baseline norm copy + //detailed results + BLOB_CHOICE_LIST_CLIST blob_choices; + + set_global_subsubloc_code(SUBSUBLOC_OTHER); + if (matcher_fp != NULL) { + word_answer = (char *) word->word->text (); + if (word_answer != NULL && word_answer[0] == '\0') + word_answer = NULL; + } + matcher_pass = 0; + bln_word = make_bln_copy (word->word, row, x_height, &word->denorm); + set_global_subsubloc_code(SUBSUBLOC_TESS); + if (tessedit_training_tess) + word->best_choice = correct_segment_pass2 (bln_word, + &word->denorm, + tess_default_matcher, + tess_training_tester, + word->raw_choice, + &blob_choices, word->outword); + else if (tessedit_dump_choices) + word->best_choice = test_segment_pass2 (bln_word, + &word->denorm, + tess_default_matcher, + choice_dump_tester, + word->raw_choice, + &blob_choices, word->outword); + // else if (tessedit_training_wiseowl) + // best_choice=correct_segment_pass2( word, &denorm, + // tess_default_matcher,wo_learn, + // raw_choice,&blob_choices,outword); + // else if (tessedit_matcher_is_wiseowl) + // best_choice=tess_segment_pass2( word, &denorm, wo_classify, + // raw_choice, &blob_choices, outword); + else { + word->best_choice = tess_segment_pass2 (bln_word, &word->denorm, + tess_default_matcher, + word->raw_choice, &blob_choices, + word->outword); + } + set_global_subsubloc_code(SUBSUBLOC_OTHER); + /* + Test for TESS screw up on word. Recog_word has already ensured that the + choice list, outword blob lists and best_choice string are the same + length. A TESS screw up is indicated by a blank filled or 0 length string. + */ + if ((word->best_choice->string ().length () == 0) || + (strspn (word->best_choice->string ().string (), " ") == + word->best_choice->string ().length ())) { + word->tess_failed = TRUE; + word->reject_map.initialise (word->best_choice->string ().length ()); + word->reject_map.rej_word_tess_failure (); + // tprintf("Empty word produced\n"); + } + else { + if ((word->best_choice->string ().length () != + word->outword->blob_list ()->length ()) || + (word->best_choice->string ().length () != blob_choices.length ())) { + tprintf + ("ASSERT FAIL String:\"%s\"; Strlen=%d; #Blobs=%d; #Choices=%d\n", + word->best_choice->string ().string (), + word->best_choice->string ().length (), + word->outword->blob_list ()->length (), blob_choices.length ()); + } + ASSERT_HOST (word->best_choice->string ().length () == + word->outword->blob_list ()->length ()); + ASSERT_HOST (word->best_choice->string ().length () == + blob_choices.length ()); + + word->tess_failed = FALSE; + if (word->word->flag (W_REP_CHAR)) { + fix_rep_char(word); + } + else { + fix_quotes ((char *) word->best_choice->string ().string (), + word->outword, &blob_choices); + if (tessedit_fix_hyphens) + fix_hyphens ((char *) word->best_choice->string ().string (), + word->outword, &blob_choices); + /* Dont trust fix_quotes! - though I think I've fixed the bug */ + if ((word->best_choice->string ().length () != + word->outword->blob_list ()->length ()) || + (word->best_choice->string ().length () != + blob_choices.length ())) { + #ifndef SECURE_NAMES + tprintf + ("POST FIX_QUOTES FAIL String:\"%s\"; Strlen=%d; #Blobs=%d; #Choices=%d\n", + word->best_choice->string ().string (), + word->best_choice->string ().length (), + word->outword->blob_list ()->length (), + blob_choices.length ()); + #endif + + } + ASSERT_HOST (word->best_choice->string ().length () == + word->outword->blob_list ()->length ()); + ASSERT_HOST (word->best_choice->string ().length () == + blob_choices.length ()); + + word->tess_accepted = tess_acceptable_word (word->best_choice, + word->raw_choice); + + make_reject_map (word, &blob_choices, row, 2); + } + } + blob_choices.deep_clear (); + delete bln_word; + assert (word->raw_choice != NULL); +} + + +/************************************************************************* + * fix_rep_char() + * The word is a repeated char. Find the repeated char character. Make a reject + * string which rejects any char other than the voted char. Set the word to done + * to stop rematching it. + * + *************************************************************************/ +void fix_rep_char( //Repeated char word + WERD_RES *word //word to do + ) { + struct REP_CH + { + char ch; + int count; + }; + + REP_CH *rep_ch; //array of char counts + int word_len; + int rep_ch_count = 0; //how many unique chs + const char *word_str; //the repeated chs + int i, j; + int total = 0; + int max = 0; + char maxch = ' '; //Most common char + + word_str = word->best_choice->string ().string (); + word_len = strlen (word_str); + rep_ch = (REP_CH *) alloc_mem (word_len * sizeof (REP_CH)); + for (i = 0; i < word_len; i++) { + for (j = 0; j < rep_ch_count && rep_ch[j].ch != word_str[i]; j++); + if (j < rep_ch_count) + rep_ch[j].count++; + else { + rep_ch[rep_ch_count].ch = word_str[i]; + rep_ch[rep_ch_count].count = 1; + rep_ch_count++; + } + } + + for (j = 0; j < rep_ch_count; j++) { + total += rep_ch[j].count; + if ((rep_ch[j].count > max) && (rep_ch[j].ch != ' ')) { + max = rep_ch[j].count; + maxch = rep_ch[j].ch; + } + } + // tprintf( "REPEATED CHAR %s len=%d total=%d choice=%c\n", + // word_str, word_len, total, maxch ); + free_mem(rep_ch); + + word->reject_map.initialise (word_len); + for (i = 0; i < word_len; i++) { + if (word_str[i] != maxch) + //rej unrecognised blobs + word->reject_map[i].setrej_bad_repetition (); + } + word->done = TRUE; +} + + +/********************************************************************** + * fix_quotes + * + * Change pairs of quotes to double quotes. + **********************************************************************/ + +void fix_quotes( //make double quotes + char *string, //string to fix + WERD *word, //word to do //char choices + BLOB_CHOICE_LIST_CLIST *blob_choices) { + char *ptr; //string ptr + //blobs + PBLOB_IT blob_it = word->blob_list (); + //choices + BLOB_CHOICE_LIST_C_IT choice_it = blob_choices; + BLOB_CHOICE_IT it1; //first choices + BLOB_CHOICE_IT it2; //second choices + + for (ptr = string; + *ptr != '\0'; ptr++, blob_it.forward (), choice_it.forward ()) { + if ((*ptr == '\'' || *ptr == '`') + && (*(ptr + 1) == '\'' || *(ptr + 1) == '`')) { + *ptr = '"'; //turn to double + strcpy (ptr + 1, ptr + 2); //shuffle up + merge_blobs (blob_it.data (), blob_it.data_relative (1)); + blob_it.forward (); + delete blob_it.extract (); //get rid of spare + + it1.set_to_list (choice_it.data ()); + it2.set_to_list (choice_it.data_relative (1)); + if (it1.data ()->certainty () < it2.data ()->certainty ()) { + choice_it.forward (); + //get rid of spare + delete choice_it.extract (); + } + else { + //get rid of spare + delete choice_it.extract (); + choice_it.forward (); + } + } + } +} + + +/********************************************************************** + * fix_hyphens + * + * Change pairs of hyphens to a single hyphen if the bounding boxes touch + * Typically a long dash which has been segmented. + **********************************************************************/ + +void fix_hyphens( //crunch double hyphens + char *string, //string to fix + WERD *word, //word to do //char choices + BLOB_CHOICE_LIST_CLIST *blob_choices) { + char *ptr; //string ptr + //blobs + PBLOB_IT blob_it = word->blob_list (); + //choices + BLOB_CHOICE_LIST_C_IT choice_it = blob_choices; + BLOB_CHOICE_IT it1; //first choices + BLOB_CHOICE_IT it2; //second choices + + for (ptr = string; + *ptr != '\0'; ptr++, blob_it.forward (), choice_it.forward ()) { + if ((*ptr == '-' || *ptr == '~') && + (*(ptr + 1) == '-' || *(ptr + 1) == '~') && + (blob_it.data ()->bounding_box ().right () >= + blob_it.data_relative (1)->bounding_box ().left ())) { + *ptr = '-'; //turn to single hyphen + strcpy (ptr + 1, ptr + 2); //shuffle up + merge_blobs (blob_it.data (), blob_it.data_relative (1)); + blob_it.forward (); + delete blob_it.extract (); //get rid of spare + + it1.set_to_list (choice_it.data ()); + it2.set_to_list (choice_it.data_relative (1)); + if (it1.data ()->certainty () < it2.data ()->certainty ()) { + choice_it.forward (); + //get rid of spare + delete choice_it.extract (); + } + else { + //get rid of spare + delete choice_it.extract (); + choice_it.forward (); + } + } + } +} + + +/********************************************************************** + * merge_blobs + * + * Add the outlines from blob2 to blob1. Blob2 is emptied but not deleted. + **********************************************************************/ + +void merge_blobs( //combine 2 blobs + PBLOB *blob1, //dest blob + PBLOB *blob2 //source blob + ) { + OUTLINE_IT outline_it = blob1->out_list (); + //iterator + + outline_it.move_to_last (); //go to end + //do it + outline_it.add_list_after (blob2->out_list ()); +} + + +/********************************************************************** + * choice_dump_tester + * + * Matcher tester function which generates .chc file entries. + * Called via test_segment_pass2 for every blob tested by tess in a word. + * (But only for words for which a correct segmentation could be found.) + **********************************************************************/ + +void choice_dump_tester( //dump chars in word + PBLOB *, //blob + DENORM *, //de-normaliser + BOOL8 correct, //ly segmented + char *text, //correct text + INT32 count, //chars in text + BLOB_CHOICE_LIST *ratings //list of results + ) { + STRING choice_file_name; + BLOB_CHOICE *blob_choice; + BLOB_CHOICE_IT it; + char source_chars[20]; + char correct_char[3]; + + if (choice_file == NULL) { + choice_file_name = imagebasename + ".chc"; + if (!(choice_file = fopen (choice_file_name.string (), "w"))) { + CANTOPENFILE.error ("choice_dump_tester", EXIT, "%s %d", + choice_file_name.string (), errno); + } + } + + if ((count == 0) || (text == NULL) || (text[0] == '\0')) { + strcpy (source_chars, "$$"); + strcpy (correct_char, "$$"); + } + else { + strncpy(source_chars, text, count); + source_chars[count] = '\0'; + if (correct) { + correct_char[0] = text[0]; + correct_char[1] = '\0'; + } + else { + strcpy (correct_char, "$$"); + } + } + fprintf (choice_file, "%s\t%s", source_chars, correct_char); + + it.set_to_list (ratings); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + blob_choice = it.data (); + if ((blob_choice->char_class () >= '!') && + (blob_choice->char_class () <= '~')) + fprintf (choice_file, "\t%c\t%f\t%f", + blob_choice->char_class (), + blob_choice->rating (), blob_choice->certainty ()); + } + fprintf (choice_file, "\n"); +} + + +/************************************************************************* + * make_bln_copy() + * + * Generate a baseline normalised copy of the source word. The copy is done so + * that whatever format the original word is in, a polygonal bln version is + * generated as output. + *************************************************************************/ + +WERD *make_bln_copy(WERD *src_word, ROW *row, float x_height, DENORM *denorm) { + WERD *result; + + // if (wordit_linearc && !src_word->flag(W_POLYGON)) + // { + // larc_word = src_word->larc_copy( row->x_height() ); + // result = larc_word->poly_copy( row->x_height() ); + // delete larc_word; + // } + // else + result = src_word->poly_copy (row->x_height ()); + + // if (tessedit_draw_words) + // { + // if ( la_win == NO_WINDOW ) + // create_la_win(); + // result->plot( la_win ); + // } + result->baseline_normalise_x (row, x_height, denorm); + return result; +} + + +ACCEPTABLE_WERD_TYPE acceptable_word_string(const char *s) { + int i = 0; + int leading_punct_count; + int upper_count = 0; + int hyphen_pos = -1; + ACCEPTABLE_WERD_TYPE word_type = AC_UNACCEPTABLE; + + if (strlen (s) > 20) + return word_type; + + /* Single Leading punctuation char*/ + + if ((s[i] != '\0') && (STRING (chs_leading_punct).contains (s[i]))) + i++; + leading_punct_count = i; + + /* Initial cap */ + while (isupper (s[i])) { + i++; + upper_count++; + } + if (upper_count > 1) + word_type = AC_UPPER_CASE; + else { + /* Lower case word, possibly with an initial cap */ + while (islower (s[i])) { + i++; + } + if (i - leading_punct_count < quality_min_initial_alphas_reqd) + goto not_a_word; + /* + Allow a single hyphen in a lower case word + - dont trust upper case - I've seen several cases of "H" -> "I-I" + */ + if (s[i] == '-') { + hyphen_pos = i++; + if (s[i] != '\0') { + while (islower (s[i])) { + i++; + } + if (i < hyphen_pos + 3) + goto not_a_word; + } + } + else { + /* Allow "'s" in NON hyphenated lower case words */ + if ((s[i] == '\'') && (s[i + 1] == 's')) + i += 2; + } + if (upper_count > 0) + word_type = AC_INITIAL_CAP; + else + word_type = AC_LOWER_CASE; + } + + /* Up to two different, constrained trailing punctuation chars */ + if ((s[i] != '\0') && (STRING (chs_trailing_punct1).contains (s[i]))) + i++; + if ((s[i] != '\0') && + (s[i - 1] != s[i]) && (STRING (chs_trailing_punct2).contains (s[i]))) + i++; + + if (s[i] != '\0') + word_type = AC_UNACCEPTABLE; + + not_a_word: + + if (word_type == AC_UNACCEPTABLE) { + /* Look for abbreviation string */ + i = 0; + if (isupper (s[0])) { + word_type = AC_UC_ABBREV; + while ((s[i] != '\0') && isupper (s[i]) && (s[i + 1] == '.')) + i += 2; + } + else if (islower (s[0])) { + word_type = AC_LC_ABBREV; + while ((s[i] != '\0') && islower (s[i]) && (s[i + 1] == '.')) + i += 2; + } + if (s[i] != '\0') + word_type = AC_UNACCEPTABLE; + } + + return word_type; +} + + +/* DEBUGGING ROUTINE */ + +BOOL8 check_debug_pt(WERD_RES *word, int location) { + BOOL8 show_map_detail = FALSE; + INT16 i; + + #ifndef SECURE_NAMES + if (!test_pt) + return FALSE; + + tessedit_rejection_debug.set_value (FALSE); + debug_x_ht_level.set_value (0); + tessedit_cluster_debug.set_value (FALSE); + nn_debug.set_value (FALSE); + nn_reject_debug.set_value (FALSE); + + if (word->word->bounding_box ().contains (FCOORD (test_pt_x, test_pt_y))) { + if (location < 0) + return TRUE; //For breakpoint use + tessedit_rejection_debug.set_value (TRUE); + debug_x_ht_level.set_value (20); + tessedit_cluster_debug.set_value (TRUE); + nn_debug.set_value (TRUE); + nn_reject_debug.set_value (TRUE); + tprintf ("\n\nTESTWD::"); + switch (location) { + case 0: + tprintf ("classify_word_pass1 start\n"); + word->word->print (debug_fp); + break; + case 10: + tprintf ("make_reject_map: initial map"); + break; + case 20: + tprintf ("make_reject_map: after NN"); + break; + case 30: + tprintf ("classify_word_pass2 - START"); + break; + case 40: + tprintf ("classify_word_pass2 - Pre Xht"); + break; + case 50: + tprintf ("classify_word_pass2 - END"); + show_map_detail = TRUE; + break; + case 60: + tprintf ("fixspace"); + break; + case 70: + tprintf ("MM pass START"); + break; + case 80: + tprintf ("MM pass END"); + break; + case 90: + tprintf ("After Poor quality rejection"); + break; + case 100: + tprintf ("unrej_good_quality_words - START"); + break; + case 110: + tprintf ("unrej_good_quality_words - END"); + break; + case 120: + tprintf ("Write results pass"); + show_map_detail = TRUE; + break; + } + tprintf (" \"%s\" ", word->best_choice->string ().string ()); + word->reject_map.print (debug_fp); + tprintf ("\n"); + if (show_map_detail) { + tprintf ("\"%s\"\n", word->best_choice->string ().string ()); + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + tprintf ("**** \"%c\" ****\n", word->best_choice->string ()[i]); + word->reject_map[i].full_print (debug_fp); + } + } + + tprintf ("Tess Accepted: %s\n", word->tess_accepted ? "TRUE" : "FALSE"); + tprintf ("Done flag: %s\n\n", word->done ? "TRUE" : "FALSE"); + return TRUE; + } + else + #endif + return FALSE; +} + + +/********************************************************************** + * set_word_fonts + * + * Get the fonts for the word. + **********************************************************************/ + +void set_word_fonts( //good chars in word + WERD_RES *word, //word to adapt to //detailed results + BLOB_CHOICE_LIST_CLIST *blob_choices) { + INT32 index; //char index + char choice_char; //char from word + INT8 config; //font of char + //character iterator + BLOB_CHOICE_LIST_C_IT char_it = blob_choices; + BLOB_CHOICE_IT choice_it; //choice iterator + STATS fonts (0, 32); //font counters + static INT8 italic_table[32] = { + 1, -1, 1, -1, + 1, -1, 1, -1, + 1, -1, 1, -1, + 1, -1, 1, -1, + 1, -1, 1, -1, + 1, -1, 1, -1, + 1, -1, 1, -1, + 1, -1, 1, -1 + }; + static INT8 bold_table[32] = { + 1, 1, -1, -1, + 1, 1, -1, -1, + 1, 1, -1, -1, + 1, 1, -1, -1, + 1, 1, -1, -1, + 1, 1, -1, -1, + 1, 1, -1, -1, + 1, 1, -1, -1 + }; + static INT8 font_table[32] = { + 2, 2, 2, 2, + -1, -1, -1, -1, + 0, 0, 0, 0, + 1, 1, 1, 1, + 3, 3, 3, 3, + 4, 4, 4, 4, + 5, 5, 5, 5, + 2, 2, 2, 2 + }; + + word->italic = 0; + word->bold = 0; + for (char_it.mark_cycle_pt (), index = 0; + !char_it.cycled_list (); char_it.forward (), index++) { + choice_char = word->best_choice->string ()[index]; + choice_it.set_to_list (char_it.data ()); + for (choice_it.mark_cycle_pt (); !choice_it.cycled_list (); + choice_it.forward ()) { + if (choice_it.data ()->char_class () == choice_char) { + config = choice_it.data ()->config (); + if (tessedit_debug_fonts) + tprintf ("%c(%d=%d%c%c)", + choice_char, config, (config & 31) >> 2, + config & 2 ? 'N' : 'B', config & 1 ? 'N' : 'I'); + if (config != -1) { + config &= 31; + word->italic += italic_table[config]; + word->bold += bold_table[config]; + if (font_table[config] != -1) + fonts.add (font_table[config], 1); + } + break; + } + } + } + find_modal_font (&fonts, &word->font1, &word->font1_count); + find_modal_font (&fonts, &word->font2, &word->font2_count); + if (tessedit_debug_fonts) + tprintf ("\n"); + /* if (word->font1_count>0) + { + for (char_it.mark_cycle_pt(),index=0; + !char_it.cycled_list();char_it.forward(),index++) + { + choice_char=word->best_choice->string()[index]; + choice_it.set_to_list(char_it.data()); + for (choice_it.mark_cycle_pt();!choice_it.cycled_list();choice_it.forward()) + { + if (choice_it.data()->char_class()==choice_char) + { + config=choice_it.data()->config(); + if (config!=-1 && font_table[config&31]==word->font1) + { + word->italic+=italic_table[config]; + word->bold+=bold_table[config]; + } + break; + } + } + } + }*/ +} + + +/********************************************************************** + * font_recognition_pass + * + * Smooth the fonts for the document. + **********************************************************************/ + +void font_recognition_pass( //good chars in word + PAGE_RES_IT &page_res_it) { + INT32 length; //of word + INT32 count; //of a feature + INT8 doc_font; //modal font + INT8 doc_font_count; //modal font + INT32 doc_italic; //total italics + INT32 doc_bold; //total bolds + ROW_RES *row = NULL; //current row + WERD_RES *word; //current word + STATS fonts (0, 32); //font counters + STATS doc_fonts (0, 32); //font counters + + doc_italic = 0; + doc_bold = 0; + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + if (row != page_res_it.row ()) { + if (row != NULL) { + find_modal_font (&fonts, &row->font1, &row->font1_count); + find_modal_font (&fonts, &row->font2, &row->font2_count); + } + row = page_res_it.row (); //current row + fonts.clear (); //clear counters + row->italic = 0; + row->bold = 0; + } + word = page_res_it.word (); + row->italic += word->italic; + row->bold += word->bold; + fonts.add (word->font1, word->font1_count); + fonts.add (word->font2, word->font2_count); + doc_italic += word->italic; + doc_bold += word->bold; + doc_fonts.add (word->font1, word->font1_count); + doc_fonts.add (word->font2, word->font2_count); + page_res_it.forward (); + } + if (row != NULL) { + find_modal_font (&fonts, &row->font1, &row->font1_count); + find_modal_font (&fonts, &row->font2, &row->font2_count); + } + find_modal_font(&doc_fonts, &doc_font, &doc_font_count); + /* + row=NULL; + page_res_it.restart_page(); + while (page_res_it.word() != NULL) + { + if (row!=page_res_it.row()) + { + row2=row; + row=page_res_it.row(); + if (row->font1_countrow->x_height()-row2->row->x_height(); + if (hdiff<0) + hdiff=-hdiff; + if (hdiffrow->x_height()-row2->row->x_height(); + if (hdiff<0) + hdiff=-hdiff; + if (hdiffitalic=italic; + row->bold=bold; + find_modal_font(&fonts,&row->font1,&row->font1_count); + find_modal_font(&fonts,&row->font2,&row->font2_count); + } + else + page_res_it.forward(); + } + else + page_res_it.forward(); + }*/ + + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + row = page_res_it.row (); //current row + word = page_res_it.word (); + length = word->best_choice->string ().length (); + + count = word->italic; + if (count < 0) + count = -count; + if (!(count == length || length > 3 && count >= length * 3 / 4)) + word->italic = doc_italic > 0 ? 1 : -1; + + count = word->bold; + if (count < 0) + count = -count; + if (!(count == length || length > 3 && count >= length * 3 / 4)) + word->bold = doc_bold > 0 ? 1 : -1; + + count = word->font1_count; + if (!(count == length || length > 3 && count >= length * 3 / 4)) { + word->font1 = doc_font; + word->font1_count = doc_font_count; + } + + page_res_it.forward (); + } +} + + +/********************************************************************** + * add_in_one_row + * + * Add into the stats for one row. + **********************************************************************/ + +void add_in_one_row( //good chars in word + ROW_RES *row, //current row + STATS *fonts, //font stats + INT8 *italic, //output count + INT8 *bold //output count + ) { + WERD_RES *word; //current word + WERD_RES_IT word_it = &row->word_res_list; + + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + *italic += word->italic; + *bold += word->bold; + if (word->font1_count > 0) + fonts->add (word->font1, word->font1_count); + if (word->font2_count > 0) + fonts->add (word->font2, word->font2_count); + + } +} + + +/********************************************************************** + * find_modal_font + * + * Find the modal font and remove from the stats. + **********************************************************************/ + +void find_modal_font( //good chars in word + STATS *fonts, //font stats + INT8 *font_out, //output font + INT8 *font_count //output count + ) { + INT8 font; //font index + INT32 count; //pile couat + + if (fonts->get_total () > 0) { + font = (INT8) fonts->mode (); + *font_out = font; + count = fonts->pile_count (font); + *font_count = count < MAX_INT8 ? count : MAX_INT8; + fonts->add (font, -*font_count); + } + else { + *font_out = -1; + *font_count = 0; + } +} diff --git a/ccmain/control.h b/ccmain/control.h new file mode 100644 index 0000000000..a660a91ea3 --- /dev/null +++ b/ccmain/control.h @@ -0,0 +1,193 @@ +/********************************************************************** + * File: control.h (Formerly control.h) + * Description: Module-independent matcher controller. + * Author: Ray Smith + * Created: Thu Apr 23 11:09:58 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef CONTROL_H +#define CONTROL_H + +#include "varable.h" +#include "ocrblock.h" +//#include "epapdest.h" +#include "ratngs.h" +#include "statistc.h" +//#include "epapconv.h" +#include "ocrshell.h" +#include "pageres.h" +#include "charsample.h" +#include "notdll.h" + +enum ACCEPTABLE_WERD_TYPE +{ + AC_UNACCEPTABLE, //Unacceptable word + AC_LOWER_CASE, //ALL lower case + AC_UPPER_CASE, //ALL upper case + AC_INITIAL_CAP, //ALL but initial lc + AC_LC_ABBREV, //a.b.c. + AC_UC_ABBREV //A.B.C. +}; + +typedef BOOL8 (*BLOB_REJECTOR) (PBLOB *, BLOB_CHOICE_IT *, void *); + +extern INT_VAR_H (tessedit_single_match, FALSE, "Top choice only from CP"); +//extern BOOL_VAR_H(tessedit_small_match,FALSE,"Use small matrix matcher"); +extern BOOL_VAR_H (tessedit_print_text, FALSE, "Write text to stdout"); +extern BOOL_VAR_H (tessedit_draw_words, FALSE, "Draw source words"); +extern BOOL_VAR_H (tessedit_draw_outwords, FALSE, "Draw output words"); +extern BOOL_VAR_H (tessedit_training_wiseowl, FALSE, +"Call WO to learn blobs"); +extern BOOL_VAR_H (tessedit_training_tess, FALSE, "Call Tess to learn blobs"); +extern BOOL_VAR_H (tessedit_matcher_is_wiseowl, FALSE, "Call WO to classify"); +extern BOOL_VAR_H (tessedit_dump_choices, FALSE, "Dump char choices"); +extern BOOL_VAR_H (tessedit_fix_fuzzy_spaces, TRUE, +"Try to improve fuzzy spaces"); +extern BOOL_VAR_H (tessedit_unrej_any_wd, FALSE, +"Dont bother with word plausibility"); +extern BOOL_VAR_H (tessedit_fix_hyphens, TRUE, "Crunch double hyphens?"); +extern BOOL_VAR_H (tessedit_reject_fullstops, FALSE, "Reject all fullstops"); +extern BOOL_VAR_H (tessedit_reject_suspect_fullstops, FALSE, +"Reject suspect fullstops"); +extern BOOL_VAR_H (tessedit_redo_xheight, TRUE, "Check/Correct x-height"); +extern BOOL_VAR_H (tessedit_cluster_adaption_on, TRUE, +"Do our own adaption - ems only"); +extern BOOL_VAR_H (tessedit_enable_doc_dict, TRUE, +"Add words to the document dictionary"); +extern BOOL_VAR_H (word_occ_first, FALSE, "Do word occ before re-est xht"); +extern BOOL_VAR_H (tessedit_xht_fiddles_on_done_wds, TRUE, +"Apply xht fix up even if done"); +extern BOOL_VAR_H (tessedit_xht_fiddles_on_no_rej_wds, TRUE, +"Apply xht fix up even in no rejects"); +extern INT_VAR_H (x_ht_check_word_occ, 2, "Check Char Block occupancy"); +extern INT_VAR_H (x_ht_stringency, 1, "How many confirmed a/n to accept?"); +extern BOOL_VAR_H (x_ht_quality_check, TRUE, "Dont allow worse quality"); +extern BOOL_VAR_H (tessedit_debug_block_rejection, FALSE, +"Block and Row stats"); +extern INT_VAR_H (debug_x_ht_level, 0, "Reestimate debug"); +extern BOOL_VAR_H (rej_use_xht, TRUE, "Individual rejection control"); +extern BOOL_VAR_H (debug_acceptable_wds, FALSE, "Dump word pass/fail chk"); +extern STRING_VAR_H (chs_leading_punct, "('`\"", "Leading punctuation"); +extern +STRING_VAR_H (chs_trailing_punct1, ").,;:?!", "1st Trailing punctuation"); +extern STRING_VAR_H (chs_trailing_punct2, ")'`\"", +"2nd Trailing punctuation"); +extern double_VAR_H (quality_rej_pc, 0.08, +"good_quality_doc lte rejection limit"); +extern double_VAR_H (quality_blob_pc, 0.0, +"good_quality_doc gte good blobs limit"); +extern double_VAR_H (quality_outline_pc, 1.0, +"good_quality_doc lte outline error limit"); +extern double_VAR_H (quality_char_pc, 0.95, +"good_quality_doc gte good char limit"); +extern INT_VAR_H (quality_min_initial_alphas_reqd, 2, +"alphas in a good word"); +extern BOOL_VAR_H (tessedit_tess_adapt_to_rejmap, FALSE, +"Use reject map to control Tesseract adaption"); +extern INT_VAR_H (tessedit_tess_adaption_mode, 3, +"Adaptation decision algorithm for tess"); +extern INT_VAR_H (tessedit_em_adaption_mode, 62, +"Adaptation decision algorithm for ems matrix matcher"); +extern BOOL_VAR_H (tessedit_cluster_adapt_after_pass1, FALSE, +"Adapt using clusterer after pass 1"); +extern BOOL_VAR_H (tessedit_cluster_adapt_after_pass2, FALSE, +"Adapt using clusterer after pass 1"); +extern BOOL_VAR_H (tessedit_cluster_adapt_after_pass3, FALSE, +"Adapt using clusterer after pass 1"); +extern BOOL_VAR_H (tessedit_cluster_adapt_before_pass1, FALSE, +"Adapt using clusterer before Tess adaping during pass 1"); +extern INT_VAR_H (tessedit_cluster_adaption_mode, 0, +"Adaptation decision algorithm for matrix matcher"); +extern BOOL_VAR_H (tessedit_adaption_debug, FALSE, +"Generate and print debug information for adaption"); +extern BOOL_VAR_H (tessedit_minimal_rej_pass1, FALSE, +"Do minimal rejection on pass 1 output"); +extern BOOL_VAR_H (tessedit_test_adaption, FALSE, +"Test adaption criteria"); +extern BOOL_VAR_H (tessedit_global_adaption, FALSE, +"Adapt to all docs over time"); +extern BOOL_VAR_H (tessedit_matcher_log, FALSE, "Log matcher activity"); +extern INT_VAR_H (tessedit_test_adaption_mode, 3, +"Adaptation decision algorithm for tess"); +extern BOOL_VAR_H (test_pt, FALSE, "Test for point"); +extern double_VAR_H (test_pt_x, 99999.99, "xcoord"); +extern double_VAR_H (test_pt_y, 99999.99, "ycoord"); +void recog_pseudo_word( //recognize blobs + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box); +BOOL8 recog_interactive( //recognize blobs + BLOCK *, //block + ROW *row, //row of word + WERD *word //word to recognize + ); +void recog_all_words( //process words + PAGE_RES *page_res, //page structure + volatile ETEXT_DESC *monitor //progress monitor + ); +void classify_word_pass1( //recog one word + WERD_RES *word, //word to do + ROW *row, + BOOL8 cluster_adapt, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); + //word to do +void classify_word_pass2(WERD_RES *word, ROW *row); +void match_word_pass2( //recog one word + WERD_RES *word, //word to do + ROW *row, + float x_height); +void fix_rep_char( //Repeated char word + WERD_RES *word //word to do + ); +void fix_quotes( //make double quotes + char *string, //string to fix + WERD *word, //word to do //char choices + BLOB_CHOICE_LIST_CLIST *blob_choices); +void fix_hyphens( //crunch double hyphens + char *string, //string to fix + WERD *word, //word to do //char choices + BLOB_CHOICE_LIST_CLIST *blob_choices); +void merge_blobs( //combine 2 blobs + PBLOB *blob1, //dest blob + PBLOB *blob2 //source blob + ); +void choice_dump_tester( //dump chars in word + PBLOB *, //blob + DENORM *, //de-normaliser + BOOL8 correct, //ly segmented + char *text, //correct text + INT32 count, //chars in text + BLOB_CHOICE_LIST *ratings //list of results + ); +WERD *make_bln_copy(WERD *src_word, ROW *row, float x_height, DENORM *denorm); +ACCEPTABLE_WERD_TYPE acceptable_word_string(const char *s); +BOOL8 check_debug_pt(WERD_RES *word, int location); +void set_word_fonts( //good chars in word + WERD_RES *word, //word to adapt to //detailed results + BLOB_CHOICE_LIST_CLIST *blob_choices); +void font_recognition_pass( //good chars in word + PAGE_RES_IT &page_res_it); +void add_in_one_row( //good chars in word + ROW_RES *row, //current row + STATS *fonts, //font stats + INT8 *italic, //output count + INT8 *bold //output count + ); +void find_modal_font( //good chars in word + STATS *fonts, //font stats + INT8 *font_out, //output font + INT8 *font_count //output count + ); +#endif diff --git a/ccmain/docqual.cpp b/ccmain/docqual.cpp new file mode 100644 index 0000000000..a889ca4ca5 --- /dev/null +++ b/ccmain/docqual.cpp @@ -0,0 +1,1453 @@ +/****************************************************************** + * File: docqual.cpp (Formerly docqual.c) + * Description: Document Quality Metrics + * Author: Phil Cheatle + * Created: Mon May 9 11:27:28 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include "docqual.h" +#include "tstruct.h" +#include "tfacep.h" +#include "reject.h" +#include "tessvars.h" +#include "genblob.h" +#include "secname.h" + +#define EXTERN + +EXTERN STRING_VAR (outlines_odd, "%| ", "Non standard number of outlines"); +EXTERN STRING_VAR (outlines_2, "ij!?%\":;", +"Non standard number of outlines"); +EXTERN BOOL_VAR (docqual_excuse_outline_errs, FALSE, +"Allow outline errs in unrejection?"); +EXTERN BOOL_VAR (tessedit_good_quality_unrej, TRUE, +"Reduce rejection on good docs"); +EXTERN BOOL_VAR (tessedit_use_reject_spaces, TRUE, "Reject spaces?"); +EXTERN double_VAR (tessedit_reject_doc_percent, 65.00, +"%rej allowed before rej whole doc"); +EXTERN double_VAR (tessedit_reject_block_percent, 45.00, +"%rej allowed before rej whole block"); +EXTERN double_VAR (tessedit_reject_row_percent, 40.00, +"%rej allowed before rej whole row"); +EXTERN double_VAR (tessedit_whole_wd_rej_row_percent, 70.00, +"%of row rejects in whole word rejects which prevents whole row rejection"); +EXTERN BOOL_VAR (tessedit_preserve_blk_rej_perfect_wds, TRUE, +"Only rej partially rejected words in block rejection"); +EXTERN BOOL_VAR (tessedit_preserve_row_rej_perfect_wds, TRUE, +"Only rej partially rejected words in row rejection"); +EXTERN BOOL_VAR (tessedit_dont_blkrej_good_wds, FALSE, +"Use word segmentation quality metric"); +EXTERN BOOL_VAR (tessedit_dont_rowrej_good_wds, FALSE, +"Use word segmentation quality metric"); +EXTERN INT_VAR (tessedit_preserve_min_wd_len, 2, +"Only preserve wds longer than this"); +EXTERN BOOL_VAR (tessedit_row_rej_good_docs, TRUE, +"Apply row rejection to good docs"); +EXTERN double_VAR (tessedit_good_doc_still_rowrej_wd, 1.1, +"rej good doc wd if more than this fraction rejected"); +EXTERN BOOL_VAR (tessedit_reject_bad_qual_wds, TRUE, +"Reject all bad quality wds"); +EXTERN BOOL_VAR (tessedit_debug_doc_rejection, FALSE, "Page stats"); +EXTERN BOOL_VAR (tessedit_debug_quality_metrics, FALSE, +"Output data to debug file"); +EXTERN BOOL_VAR (bland_unrej, FALSE, "unrej potential with no chekcs"); +EXTERN double_VAR (quality_rowrej_pc, 1.1, +"good_quality_doc gte good char limit"); + +EXTERN BOOL_VAR (unlv_tilde_crunching, TRUE, +"Mark v.bad words for tilde crunch"); +EXTERN BOOL_VAR (crunch_early_merge_tess_fails, TRUE, "Before word crunch?"); +EXTERN BOOL_EVAR (crunch_early_convert_bad_unlv_chs, FALSE, +"Take out ~^ early?"); + +EXTERN double_VAR (crunch_terrible_rating, 80.0, "crunch rating lt this"); +EXTERN BOOL_VAR (crunch_terrible_garbage, TRUE, "As it says"); +EXTERN double_VAR (crunch_poor_garbage_cert, -9.0, +"crunch garbage cert lt this"); +EXTERN double_VAR (crunch_poor_garbage_rate, 60, +"crunch garbage rating lt this"); + +EXTERN double_VAR (crunch_pot_poor_rate, 40, +"POTENTIAL crunch rating lt this"); +EXTERN double_VAR (crunch_pot_poor_cert, -8.0, +"POTENTIAL crunch cert lt this"); +EXTERN BOOL_VAR (crunch_pot_garbage, TRUE, "POTENTIAL crunch garbage"); + +EXTERN double_VAR (crunch_del_rating, 60, "POTENTIAL crunch rating lt this"); +EXTERN double_VAR (crunch_del_cert, -10.0, "POTENTIAL crunch cert lt this"); +EXTERN double_VAR (crunch_del_min_ht, 0.7, "Del if word ht lt xht x this"); +EXTERN double_VAR (crunch_del_max_ht, 3.0, "Del if word ht gt xht x this"); +EXTERN double_VAR (crunch_del_min_width, 3.0, +"Del if word width lt xht x this"); +EXTERN double_VAR (crunch_del_high_word, 1.5, +"Del if word gt xht x this above bl"); +EXTERN double_VAR (crunch_del_low_word, 0.5, +"Del if word gt xht x this below bl"); +EXTERN double_VAR (crunch_small_outlines_size, 0.6, "Small if lt xht x this"); + +EXTERN INT_VAR (crunch_rating_max, 10, "For adj length in rating per ch"); +EXTERN INT_VAR (crunch_pot_indicators, 1, +"How many potential indicators needed"); + +EXTERN BOOL_VAR (crunch_leave_ok_strings, TRUE, +"Dont touch sensible strings"); +EXTERN BOOL_VAR (crunch_accept_ok, TRUE, "Use acceptability in okstring"); +EXTERN BOOL_VAR (crunch_leave_accept_strings, FALSE, +"Dont pot crunch sensible strings"); +EXTERN BOOL_VAR (crunch_include_numerals, FALSE, "Fiddle alpha figures"); +EXTERN INT_VAR (crunch_leave_lc_strings, 4, +"Dont crunch words with long lower case strings"); +EXTERN INT_VAR (crunch_leave_uc_strings, 4, +"Dont crunch words with long lower case strings"); +EXTERN INT_VAR (crunch_long_repetitions, 3, +"Crunch words with long repetitions"); + +EXTERN INT_VAR (crunch_debug, 0, "As it says"); + +/************************************************************************* + * word_blob_quality() + * How many blobs in the outword are identical to those of the inword? + * ASSUME blobs in both initial word and outword are in ascending order of + * left hand blob edge. + *************************************************************************/ +INT16 word_blob_quality( //Blob seg changes + WERD_RES *word, + ROW *row) { + WERD *bln_word; //BL norm init word + TWERD *tessword; //tess format + WERD *init_word; //BL norm init word + PBLOB_IT outword_it; + PBLOB_IT initial_it; + INT16 i; + INT16 init_blobs_left; + INT16 match_count = 0; + BOOL8 matched; + BOX out_box; + PBLOB *test_blob; + DENORM denorm; + float bln_xht; + + if (word->word->gblob_list ()->empty ()) + return 0; + //xht used for blnorm + bln_xht = bln_x_height / word->denorm.scale (); + bln_word = make_bln_copy (word->word, row, bln_xht, &denorm); + /* + NOTE: Need to convert to tess format and back again to ensure that the + same float -> int rounding of coords is done to source wd as out wd before + comparison + */ + // if (!bln_word->flag(W_POLYGON)) + // tprintf( "NON POLYGON BLN WERD\n"); + tessword = make_tess_word (bln_word, NULL); + //convert word + init_word = make_ed_word (tessword, bln_word); + // if (!init_word->flag(W_POLYGON)) + // tprintf( "NON POLYGON INIT WERD\n"); + // tprintf( "SOURCE BLOBS-AFTER TESS:\n"); + // print_boxes( init_word ); + // tprintf( "OUTPUT BLOBS:\n"); + // print_boxes( word->outword ); + + initial_it.set_to_list (init_word->blob_list ()); + init_blobs_left = initial_it.length (); + outword_it.set_to_list (word->outword->blob_list ()); + delete bln_word; + delete_word(tessword); //get rid of it + + for (outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); outword_it.forward ()) { + out_box = outword_it.data ()->bounding_box (); + + /* Skip any initial blobs LEFT of current outword blob */ + while (!initial_it.at_last () && + (initial_it.data ()->bounding_box ().left () < out_box.left ())) { + initial_it.forward (); + init_blobs_left--; + } + + /* See if current outword blob matches any initial blob with the same left + coord. (Normally only one but possibly more - in unknown order) */ + + i = 0; + matched = FALSE; + do { + test_blob = initial_it.data_relative (i++); + matched = crude_match_blobs (test_blob, outword_it.data ()); + if (matched) + match_count++; + } + while (!matched && + (init_blobs_left - i > 0) && + (i < 129) && + !initial_it.at_last () && + test_blob->bounding_box ().left () == out_box.left ()); + } + delete init_word; + return match_count; +} + + +/************************************************************************* + * crude_match_blobs() + * Check bounding boxes are the same and the number of outlines are the same. + *************************************************************************/ +BOOL8 crude_match_blobs(PBLOB *blob1, PBLOB *blob2) { + BOX box1 = blob1->bounding_box (); + BOX box2 = blob2->bounding_box (); + + if (box1.contains (box2) && + box2.contains (box1) && + (blob1->out_list ()->length () == blob1->out_list ()->length ())) + return TRUE; + else + return FALSE; +} + + +INT16 word_outline_errs( //Outline count errs + WERD_RES *word) { + PBLOB_IT outword_it; + INT16 i = 0; + INT16 err_count = 0; + + outword_it.set_to_list (word->outword->blob_list ()); + + for (outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); outword_it.forward ()) { + err_count += count_outline_errs (word->best_choice->string ()[i], + outword_it.data ()->out_list ()-> + length ()); + i++; + } + return err_count; +} + + +/************************************************************************* + * word_char_quality() + * Combination of blob quality and outline quality - how many good chars are + * there? - I.e chars which pass the blob AND outline tests. + *************************************************************************/ +void word_char_quality( //Blob seg changes + WERD_RES *word, + ROW *row, + INT16 *match_count, + INT16 *accepted_match_count) { + WERD *bln_word; //BL norm init word + TWERD *tessword; //tess format + WERD *init_word; //BL norm init word + PBLOB_IT outword_it; + PBLOB_IT initial_it; + INT16 i; + INT16 init_blobs_left; + BOOL8 matched; + BOX out_box; + PBLOB *test_blob; + DENORM denorm; + float bln_xht; + INT16 j = 0; + + *match_count = 0; + *accepted_match_count = 0; + if (word->word->gblob_list ()->empty ()) + return; + + //xht used for blnorm + bln_xht = bln_x_height / word->denorm.scale (); + bln_word = make_bln_copy (word->word, row, bln_xht, &denorm); + /* + NOTE: Need to convert to tess format and back again to ensure that the + same float -> int rounding of coords is done to source wd as out wd before + comparison + */ + tessword = make_tess_word (bln_word, NULL); + //convert word + init_word = make_ed_word (tessword, bln_word); + delete bln_word; + delete_word(tessword); //get rid of it + // tprintf( "SOURCE BLOBS-AFTER TESS:\n"); + // print_boxes( init_word ); + // tprintf( "OUTPUT BLOBS:\n"); + // print_boxes( word->outword ); + + initial_it.set_to_list (init_word->blob_list ()); + init_blobs_left = initial_it.length (); + outword_it.set_to_list (word->outword->blob_list ()); + + for (outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); outword_it.forward ()) { + out_box = outword_it.data ()->bounding_box (); + + /* Skip any initial blobs LEFT of current outword blob */ + while (!initial_it.at_last () && + (initial_it.data ()->bounding_box ().left () < out_box.left ())) { + initial_it.forward (); + init_blobs_left--; + } + + /* See if current outword blob matches any initial blob with the same left + coord. (Normally only one but possibly more - in unknown order) */ + + i = 0; + matched = FALSE; + do { + test_blob = initial_it.data_relative (i++); + matched = crude_match_blobs (test_blob, outword_it.data ()); + if (matched && + (count_outline_errs (word->best_choice->string ()[j], + outword_it.data ()->out_list ()->length ()) + == 0)) { + (*match_count)++; + if (word->reject_map[j].accepted ()) + (*accepted_match_count)++; + } + } + while (!matched && + (init_blobs_left - i > 0) && + (i < 129) && + !initial_it.at_last () && + test_blob->bounding_box ().left () == out_box.left ()); + j++; + } + delete init_word; +} + + +/************************************************************************* + * unrej_good_chs() + * Unreject POTENTIAL rejects if the blob passes the blob and outline checks + *************************************************************************/ +void unrej_good_chs(WERD_RES *word, ROW *row) { + WERD *bln_word; //BL norm init word + TWERD *tessword; //tess format + WERD *init_word; //BL norm init word + PBLOB_IT outword_it; + PBLOB_IT initial_it; + INT16 i; + INT16 init_blobs_left; + BOOL8 matched; + BOX out_box; + PBLOB *test_blob; + DENORM denorm; + float bln_xht; + INT16 j = 0; + + if (word->word->gblob_list ()->empty ()) + return; + + //xht used for blnorm + bln_xht = bln_x_height / word->denorm.scale (); + bln_word = make_bln_copy (word->word, row, bln_xht, &denorm); + /* + NOTE: Need to convert to tess format and back again to ensure that the + same float -> int rounding of coords is done to source wd as out wd before + comparison + */ + tessword = make_tess_word (bln_word, NULL); + //convert word + init_word = make_ed_word (tessword, bln_word); + delete bln_word; + delete_word(tessword); //get rid of it + + initial_it.set_to_list (init_word->blob_list ()); + init_blobs_left = initial_it.length (); + outword_it.set_to_list (word->outword->blob_list ()); + + for (outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); outword_it.forward ()) { + out_box = outword_it.data ()->bounding_box (); + + /* Skip any initial blobs LEFT of current outword blob */ + while (!initial_it.at_last () && + (initial_it.data ()->bounding_box ().left () < out_box.left ())) { + initial_it.forward (); + init_blobs_left--; + } + + /* See if current outword blob matches any initial blob with the same left + coord. (Normally only one but possibly more - in unknown order) */ + + i = 0; + matched = FALSE; + do { + test_blob = initial_it.data_relative (i++); + matched = crude_match_blobs (test_blob, outword_it.data ()); + if (matched && + (word->reject_map[j].accept_if_good_quality ()) && + (docqual_excuse_outline_errs || + (count_outline_errs (word->best_choice->string ()[j], + outword_it.data ()->out_list ()-> + length ()) == 0))) + word->reject_map[j].setrej_quality_accept (); + } + while (!matched && + (init_blobs_left - i > 0) && + (i < 129) && + !initial_it.at_last () && + test_blob->bounding_box ().left () == out_box.left ()); + j++; + } + delete init_word; +} + + +void print_boxes(WERD *word) { + PBLOB_IT it; + BOX box; + + it.set_to_list (word->blob_list ()); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + box = it.data ()->bounding_box (); + box.print (); + } +} + + +INT16 count_outline_errs(char c, INT16 outline_count) { + int expected_outline_count; + + if (STRING (outlines_odd).contains (c)) + return 0; //Dont use this char + else if (STRING (outlines_2).contains (c)) + expected_outline_count = 2; + else + expected_outline_count = 1; + return abs (outline_count - expected_outline_count); +} + + +void quality_based_rejection(PAGE_RES_IT &page_res_it, + BOOL8 good_quality_doc) { + if ((tessedit_good_quality_unrej && good_quality_doc)) + unrej_good_quality_words(page_res_it); + doc_and_block_rejection(page_res_it, good_quality_doc); + + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + insert_rej_cblobs (page_res_it.word ()); + page_res_it.forward (); + } + + if (unlv_tilde_crunching) { + tilde_crunch(page_res_it); + tilde_delete(page_res_it); + } +} + + +/************************************************************************* + * unrej_good_quality_words() + * Accept potential rejects in words which pass the following checks: + * - Contains a potential reject + * - Word looks like a sensible alpha word. + * - Word segmentation is the same as the original image + * - All characters have the expected number of outlines + * NOTE - the rejection counts are recalculated after unrejection + * - CANT do it in a single pass without a bit of fiddling + * - keep it simple but inefficient + *************************************************************************/ +void unrej_good_quality_words( //unreject potential + PAGE_RES_IT &page_res_it) { + WERD_RES *word; + ROW_RES *current_row; + BLOCK_RES *current_block; + int i; + + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + check_debug_pt (page_res_it.word (), 100); + if (bland_unrej) { + word = page_res_it.word (); + for (i = 0; i < word->reject_map.length (); i++) { + if (word->reject_map[i].accept_if_good_quality ()) + word->reject_map[i].setrej_quality_accept (); + } + page_res_it.forward (); + } + else if ((page_res_it.row ()->char_count > 0) && + ((page_res_it.row ()->rej_count / + (float) page_res_it.row ()->char_count) <= + quality_rowrej_pc)) { + word = page_res_it.word (); + if (word->reject_map.quality_recoverable_rejects () && + (tessedit_unrej_any_wd || + acceptable_word_string (word->best_choice->string ().string ()) + != AC_UNACCEPTABLE)) { + unrej_good_chs (word, page_res_it.row ()->row); + } + page_res_it.forward (); + } + else { + /* Skip to end of dodgy row */ + current_row = page_res_it.row (); + while ((page_res_it.word () != NULL) && + (page_res_it.row () == current_row)) + page_res_it.forward (); + } + check_debug_pt (page_res_it.word (), 110); + } + page_res_it.restart_page (); + page_res_it.page_res->char_count = 0; + page_res_it.page_res->rej_count = 0; + current_block = NULL; + current_row = NULL; + while (page_res_it.word () != NULL) { + if (current_block != page_res_it.block ()) { + current_block = page_res_it.block (); + current_block->char_count = 0; + current_block->rej_count = 0; + } + if (current_row != page_res_it.row ()) { + current_row = page_res_it.row (); + current_row->char_count = 0; + current_row->rej_count = 0; + current_row->whole_word_rej_count = 0; + } + page_res_it.rej_stat_word (); + page_res_it.forward (); + } +} + + +/************************************************************************* + * doc_and_block_rejection() + * + * If the page has too many rejects - reject all of it. + * If any block has too many rejects - reject all words in the block + *************************************************************************/ + +void doc_and_block_rejection( //reject big chunks + PAGE_RES_IT &page_res_it, + BOOL8 good_quality_doc) { + INT16 block_no = 0; + INT16 row_no = 0; + BLOCK_RES *current_block; + ROW_RES *current_row; + + BOOL8 rej_word; + BOOL8 prev_word_rejected; + INT16 char_quality; + INT16 accepted_char_quality; + + if ((page_res_it.page_res->rej_count * 100.0 / + page_res_it.page_res->char_count) > tessedit_reject_doc_percent) { + reject_whole_page(page_res_it); + #ifndef SECURE_NAMES + if (tessedit_debug_doc_rejection) { + tprintf ("REJECT ALL #chars: %d #Rejects: %d; \n", + page_res_it.page_res->char_count, + page_res_it.page_res->rej_count); + } + #endif + } + else { + #ifndef SECURE_NAMES + if (tessedit_debug_doc_rejection) + tprintf ("NO PAGE REJECTION #chars: %d # Rejects: %d; \n", + page_res_it.page_res->char_count, + page_res_it.page_res->rej_count); + #endif + + /* Walk blocks testing for block rejection */ + + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + current_block = page_res_it.block (); + if (current_block->block->text_region () != NULL) + block_no = current_block->block->text_region ()->id_no (); + else + block_no = -1; + if ((page_res_it.block ()->char_count > 0) && + ((page_res_it.block ()->rej_count * 100.0 / + page_res_it.block ()->char_count) > + tessedit_reject_block_percent)) { + #ifndef SECURE_NAMES + if (tessedit_debug_block_rejection) + tprintf ("REJECTING BLOCK %d #chars: %d; #Rejects: %d\n", + block_no, + page_res_it.block ()->char_count, + page_res_it.block ()->rej_count); + #endif + prev_word_rejected = FALSE; + while ((page_res_it.word () != NULL) && + (page_res_it.block () == current_block)) { + if (tessedit_preserve_blk_rej_perfect_wds) { + rej_word = + (page_res_it.word ()->reject_map.reject_count () > 0) + || (page_res_it.word ()->reject_map.length () < + tessedit_preserve_min_wd_len); + if (rej_word && tessedit_dont_blkrej_good_wds + && !(page_res_it.word ()->reject_map.length () < + tessedit_preserve_min_wd_len) + && + (acceptable_word_string + (page_res_it.word ()->best_choice->string (). + string ()) != AC_UNACCEPTABLE)) { + word_char_quality (page_res_it.word (), + page_res_it.row ()->row, + &char_quality, + &accepted_char_quality); + rej_word = char_quality != + page_res_it.word ()->reject_map.length (); + } + } + else + rej_word = TRUE; + if (rej_word) { + /* + Reject spacing if both current and prev words are rejected. + NOTE - this is NOT restricted to FUZZY spaces. - When tried this generated + more space errors. + */ + if (tessedit_use_reject_spaces && + prev_word_rejected && + (page_res_it.prev_row () == page_res_it.row ()) && + (page_res_it.word ()->word->space () == 1)) + page_res_it.word ()->reject_spaces = TRUE; + page_res_it.word ()->reject_map.rej_word_block_rej (); + } + prev_word_rejected = rej_word; + page_res_it.forward (); + } + } + else { + #ifndef SECURE_NAMES + if (tessedit_debug_block_rejection) + tprintf + ("NOT REJECTING BLOCK %d #chars: %d # Rejects: %d; \n", + block_no, page_res_it.block ()->char_count, + page_res_it.block ()->rej_count); + #endif + + /* Walk rows in block testing for row rejection */ + row_no = 0; + while ((page_res_it.word () != NULL) && + (page_res_it.block () == current_block)) { + current_row = page_res_it.row (); + row_no++; + /* Reject whole row if: + fraction of chars on row which are rejected exceed a limit AND + fraction rejects which occur in WHOLE WERD rejects is LESS THAN a limit + */ + if ((page_res_it.row ()->char_count > 0) && + ((page_res_it.row ()->rej_count * 100.0 / + page_res_it.row ()->char_count) > + tessedit_reject_row_percent) && + ((page_res_it.row ()->whole_word_rej_count * 100.0 / + page_res_it.row ()->rej_count) < + tessedit_whole_wd_rej_row_percent)) { + #ifndef SECURE_NAMES + if (tessedit_debug_block_rejection) + tprintf + ("REJECTING ROW %d #chars: %d; #Rejects: %d\n", + row_no, page_res_it.row ()->char_count, + page_res_it.row ()->rej_count); + #endif + prev_word_rejected = FALSE; + while ((page_res_it.word () != NULL) && + (page_res_it.row () == current_row)) { + /* Preserve words on good docs unless they are mostly rejected*/ + if (!tessedit_row_rej_good_docs && good_quality_doc) { + rej_word = + page_res_it.word ()->reject_map. + reject_count () / + (float) page_res_it.word ()->reject_map. + length () > tessedit_good_doc_still_rowrej_wd; + } + + /* Preserve perfect words anyway */ + else if (tessedit_preserve_row_rej_perfect_wds) { + rej_word = + (page_res_it.word ()->reject_map. + reject_count () > 0) + || (page_res_it.word ()->reject_map. + length () < tessedit_preserve_min_wd_len); + if (rej_word && tessedit_dont_rowrej_good_wds + && !(page_res_it.word ()->reject_map. + length () < + tessedit_preserve_min_wd_len) + && + (acceptable_word_string + (page_res_it.word ()->best_choice-> + string ().string ()) != AC_UNACCEPTABLE)) { + word_char_quality (page_res_it.word (), + page_res_it.row ()->row, + &char_quality, + &accepted_char_quality); + rej_word = char_quality != + page_res_it.word ()->reject_map.length (); + } + } + else + rej_word = TRUE; + if (rej_word) { + /* + Reject spacing if both current and prev words are rejected. + NOTE - this is NOT restricted to FUZZY spaces. - When tried this generated + more space errors. + */ + if (tessedit_use_reject_spaces && + prev_word_rejected && + (page_res_it.prev_row () == + page_res_it.row ()) + && (page_res_it.word ()->word->space () == + 1)) + page_res_it.word ()->reject_spaces = TRUE; + page_res_it.word ()->reject_map. + rej_word_row_rej(); + } + prev_word_rejected = rej_word; + page_res_it.forward (); + } + } + else { + #ifndef SECURE_NAMES + if (tessedit_debug_block_rejection) + tprintf + ("NOT REJECTING ROW %d #chars: %d # Rejects: %d; \n", + row_no, page_res_it.row ()->char_count, + page_res_it.row ()->rej_count); + #endif + while ((page_res_it.word () != NULL) && + (page_res_it.row () == current_row)) + page_res_it.forward (); + } + } + } + } + } +} + + +/************************************************************************* + * reject_whole_page() + * Dont believe any of it - set the reject map to 00..00 in all words + * + *************************************************************************/ + +void reject_whole_page(PAGE_RES_IT &page_res_it) { + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + page_res_it.word ()->reject_map.rej_word_doc_rej (); + page_res_it.forward (); + } + //whole page is rejected + page_res_it.page_res->rejected = TRUE; +} + + +void tilde_crunch(PAGE_RES_IT &page_res_it) { + WERD_RES *word; + GARBAGE_LEVEL garbage_level; + PAGE_RES_IT copy_it; + BOOL8 prev_potential_marked = FALSE; + BOOL8 found_terrible_word = FALSE; + int dict_type; + BOOL8 ok_dict_word; + + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + word = page_res_it.word (); + + if (crunch_early_convert_bad_unlv_chs) + convert_bad_unlv_chs(word); + + if (crunch_early_merge_tess_fails) + merge_tess_fails(word); + + if (word->reject_map.accept_count () != 0) { + found_terrible_word = FALSE; + //Forget earlier potential crunches + prev_potential_marked = FALSE; + } + else { + dict_type = dict_word (word->best_choice->string ().string ()); + ok_dict_word = (dict_type > 0) && (dict_type != DOC_DAWG_PERM); + garbage_level = garbage_word (word, ok_dict_word); + + if ((garbage_level != G_NEVER_CRUNCH) && + (terrible_word_crunch (word, garbage_level))) { + if (crunch_debug > 0) { + tprintf ("T CRUNCHING: \"%s\"\n", + word->best_choice->string ().string ()); + } + word->unlv_crunch_mode = CR_KEEP_SPACE; + if (prev_potential_marked) { + while (copy_it.word () != word) { + if (crunch_debug > 0) { + tprintf ("P1 CRUNCHING: \"%s\"\n", + copy_it.word ()->best_choice->string (). + string ()); + } + copy_it.word ()->unlv_crunch_mode = CR_KEEP_SPACE; + copy_it.forward (); + } + prev_potential_marked = FALSE; + } + found_terrible_word = TRUE; + } + else if ((garbage_level != G_NEVER_CRUNCH) && + (potential_word_crunch (word, + garbage_level, ok_dict_word))) { + if (found_terrible_word) { + if (crunch_debug > 0) { + tprintf ("P2 CRUNCHING: \"%s\"\n", + word->best_choice->string ().string ()); + } + word->unlv_crunch_mode = CR_KEEP_SPACE; + } + else if (!prev_potential_marked) { + copy_it = page_res_it; + prev_potential_marked = TRUE; + if (crunch_debug > 1) { + tprintf ("P3 CRUNCHING: \"%s\"\n", + word->best_choice->string ().string ()); + } + } + } + else { + found_terrible_word = FALSE; + //Forget earlier potential crunches + prev_potential_marked = FALSE; + if (crunch_debug > 2) { + tprintf ("NO CRUNCH: \"%s\"\n", + word->best_choice->string ().string ()); + } + } + } + page_res_it.forward (); + } +} + + +BOOL8 terrible_word_crunch(WERD_RES *word, GARBAGE_LEVEL garbage_level) { + float rating_per_ch; + int adjusted_len; + int crunch_mode = 0; + + if ((word->best_choice->string ().length () == 0) || + (strspn (word->best_choice->string ().string (), " ") == + word->best_choice->string ().length ())) + crunch_mode = 1; + else { + adjusted_len = word->reject_map.length (); + if (adjusted_len > crunch_rating_max) + adjusted_len = crunch_rating_max; + rating_per_ch = word->best_choice->rating () / adjusted_len; + + if (rating_per_ch > crunch_terrible_rating) + crunch_mode = 2; + else if (crunch_terrible_garbage && (garbage_level == G_TERRIBLE)) + crunch_mode = 3; + else if ((word->best_choice->certainty () < crunch_poor_garbage_cert) && + (garbage_level != G_OK)) + crunch_mode = 4; + else if ((rating_per_ch > crunch_poor_garbage_rate) && + (garbage_level != G_OK)) + crunch_mode = 5; + } + if (crunch_mode > 0) { + if (crunch_debug > 2) { + tprintf ("Terrible_word_crunch (%d) on \"%s\"\n", + crunch_mode, word->best_choice->string ().string ()); + } + return TRUE; + } + else + return FALSE; +} + + +BOOL8 potential_word_crunch(WERD_RES *word, + GARBAGE_LEVEL garbage_level, + BOOL8 ok_dict_word) { + float rating_per_ch; + int adjusted_len; + char *str = (char *) word->best_choice->string ().string (); + BOOL8 word_crunchable; + int poor_indicator_count = 0; + + word_crunchable = + !crunch_leave_accept_strings || + (word->reject_map.length () < 3) || + ((acceptable_word_string (str) == AC_UNACCEPTABLE) && !ok_dict_word); + + adjusted_len = word->reject_map.length (); + if (adjusted_len > 10) + adjusted_len = 10; + rating_per_ch = word->best_choice->rating () / adjusted_len; + + if (rating_per_ch > crunch_pot_poor_rate) { + if (crunch_debug > 2) { + tprintf ("Potential poor rating on \"%s\"\n", + word->best_choice->string ().string ()); + } + poor_indicator_count++; + } + + if (word_crunchable && + (word->best_choice->certainty () < crunch_pot_poor_cert)) { + if (crunch_debug > 2) { + tprintf ("Potential poor cert on \"%s\"\n", + word->best_choice->string ().string ()); + } + poor_indicator_count++; + } + + if (garbage_level != G_OK) { + if (crunch_debug > 2) { + tprintf ("Potential garbage on \"%s\"\n", + word->best_choice->string ().string ()); + } + poor_indicator_count++; + } + return (poor_indicator_count >= crunch_pot_indicators); +} + + +void tilde_delete(PAGE_RES_IT &page_res_it) { + WERD_RES *word; + PAGE_RES_IT copy_it; + BOOL8 deleting_from_bol = FALSE; + BOOL8 marked_delete_point = FALSE; + INT16 debug_delete_mode; + CRUNCH_MODE delete_mode; + INT16 x_debug_delete_mode; + CRUNCH_MODE x_delete_mode; + + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + word = page_res_it.word (); + + delete_mode = word_deletable (word, debug_delete_mode); + if (delete_mode != CR_NONE) { + if (word->word->flag (W_BOL) || deleting_from_bol) { + if (crunch_debug > 0) { + tprintf ("BOL CRUNCH DELETING(%d): \"%s\"\n", + debug_delete_mode, + word->best_choice->string ().string ()); + } + word->unlv_crunch_mode = delete_mode; + deleting_from_bol = TRUE; + } + else if (word->word->flag (W_EOL)) { + if (marked_delete_point) { + while (copy_it.word () != word) { + x_delete_mode = word_deletable (copy_it.word (), + x_debug_delete_mode); + if (crunch_debug > 0) { + tprintf ("EOL CRUNCH DELETING(%d): \"%s\"\n", + x_debug_delete_mode, + copy_it.word ()->best_choice->string (). + string ()); + } + copy_it.word ()->unlv_crunch_mode = x_delete_mode; + copy_it.forward (); + } + } + if (crunch_debug > 0) { + tprintf ("EOL CRUNCH DELETING(%d): \"%s\"\n", + debug_delete_mode, + word->best_choice->string ().string ()); + } + word->unlv_crunch_mode = delete_mode; + deleting_from_bol = FALSE; + marked_delete_point = FALSE; + } + else { + if (!marked_delete_point) { + copy_it = page_res_it; + marked_delete_point = TRUE; + } + } + } + else { + deleting_from_bol = FALSE; + //Forget earlier potential crunches + marked_delete_point = FALSE; + } + /* + The following step has been left till now as the tess fails are used to + determine if the word is deletable. + */ + if (!crunch_early_merge_tess_fails) + merge_tess_fails(word); + page_res_it.forward (); + } +} + + +void convert_bad_unlv_chs( //word to do + WERD_RES *word_res) { + char *ptr; //string ptr + int i; + + ptr = (char *) word_res->best_choice->string ().string (); + for (i = 0; i < word_res->reject_map.length (); i++) { + if (ptr[i] == '~') { + ptr[i] = '-'; + if (word_res->reject_map[i].accepted ()) + word_res->reject_map[i].setrej_unlv_rej (); + } + if (ptr[i] == '^') { + ptr[i] = ' '; + if (word_res->reject_map[i].accepted ()) + word_res->reject_map[i].setrej_unlv_rej (); + } + } +} + + +/********************************************************************** + * merge_tess_fails + * + * Change pairs of tess failures to a single one + **********************************************************************/ + +void merge_tess_fails( //word to do + WERD_RES *word_res) { + char *ptr; //string ptr + PBLOB_IT blob_it; //blobs + int i = 0; + int len; + + len = strlen (word_res->best_choice->string ().string ()); + ASSERT_HOST (word_res->reject_map.length () == len); + ASSERT_HOST (word_res->outword->blob_list ()->length () == len); + + ptr = (char *) word_res->best_choice->string ().string (); + blob_it = word_res->outword->blob_list (); + while (*ptr != '\0') { + if ((*ptr == ' ') && (*(ptr + 1) == ' ')) { + strcpy (ptr + 1, ptr + 2); //shuffle up + word_res->reject_map.remove_pos (i); + merge_blobs (blob_it.data_relative (1), blob_it.data ()); + delete blob_it.extract (); //get rid of spare + } + else { + i++; + ptr++; + } + blob_it.forward (); + } + len = strlen (word_res->best_choice->string ().string ()); + ASSERT_HOST (word_res->reject_map.length () == len); + ASSERT_HOST (word_res->outword->blob_list ()->length () == len); +} + + +GARBAGE_LEVEL garbage_word(WERD_RES *word, BOOL8 ok_dict_word) { + enum STATES + { + JUNK, + FIRST_UPPER, + FIRST_LOWER, + FIRST_NUM, + SUBSEQUENT_UPPER, + SUBSEQUENT_LOWER, + SUBSEQUENT_NUM + }; + char *str = (char *) word->best_choice->string ().string (); + STATES state = JUNK; + int len = 0; + int isolated_digits = 0; + int isolated_alphas = 0; + int bad_char_count = 0; + int tess_rejs = 0; + int dodgy_chars = 0; + int ok_chars; + char last_char = ' '; + int alpha_repetition_count = 0; + int longest_alpha_repetition_count = 0; + int longest_lower_run_len = 0; + int lower_string_count = 0; + int longest_upper_run_len = 0; + int upper_string_count = 0; + int total_alpha_count = 0; + int total_digit_count = 0; + + for (; *str != '\0'; str++) { + len++; + if (isupper (*str)) { + total_alpha_count++; + switch (state) { + case SUBSEQUENT_UPPER: + case FIRST_UPPER: + state = SUBSEQUENT_UPPER; + upper_string_count++; + if (longest_upper_run_len < upper_string_count) + longest_upper_run_len = upper_string_count; + if (last_char == *str) { + alpha_repetition_count++; + if (longest_alpha_repetition_count < alpha_repetition_count) { + longest_alpha_repetition_count = alpha_repetition_count; + } + } + else { + last_char = *str; + alpha_repetition_count = 1; + } + break; + case FIRST_NUM: + isolated_digits++; + default: + state = FIRST_UPPER; + last_char = *str; + alpha_repetition_count = 1; + upper_string_count = 1; + break; + } + } + else if (islower (*str)) { + total_alpha_count++; + switch (state) { + case SUBSEQUENT_LOWER: + case FIRST_LOWER: + state = SUBSEQUENT_LOWER; + lower_string_count++; + if (longest_lower_run_len < lower_string_count) + longest_lower_run_len = lower_string_count; + if (last_char == *str) { + alpha_repetition_count++; + if (longest_alpha_repetition_count < alpha_repetition_count) { + longest_alpha_repetition_count = alpha_repetition_count; + } + } + else { + last_char = *str; + alpha_repetition_count = 1; + } + break; + case FIRST_NUM: + isolated_digits++; + default: + state = FIRST_LOWER; + last_char = *str; + alpha_repetition_count = 1; + lower_string_count = 1; + break; + } + } + else if (isdigit (*str)) { + total_digit_count++; + switch (state) { + case FIRST_NUM: + state = SUBSEQUENT_NUM; + case SUBSEQUENT_NUM: + break; + case FIRST_UPPER: + case FIRST_LOWER: + isolated_alphas++; + default: + state = FIRST_NUM; + break; + } + } + else { + if (*str == ' ') + tess_rejs++; + else + bad_char_count++; + switch (state) { + case FIRST_NUM: + isolated_digits++; + break; + case FIRST_UPPER: + case FIRST_LOWER: + isolated_alphas++; + default: + break; + } + state = JUNK; + } + } + + switch (state) { + case FIRST_NUM: + isolated_digits++; + break; + case FIRST_UPPER: + case FIRST_LOWER: + isolated_alphas++; + default: + break; + } + + if (crunch_include_numerals) { + total_alpha_count += total_digit_count - isolated_digits; + } + + if (crunch_leave_ok_strings && + (len >= 4) && + (2 * (total_alpha_count - isolated_alphas) > len) && + (longest_alpha_repetition_count < crunch_long_repetitions)) { + if ((crunch_accept_ok && + (acceptable_word_string (str) != AC_UNACCEPTABLE)) || + (longest_lower_run_len > crunch_leave_lc_strings) || + (longest_upper_run_len > crunch_leave_uc_strings)) + return G_NEVER_CRUNCH; + } + if ((word->reject_map.length () > 1) && + (strpbrk (str, " ") == NULL) && + ((word->best_choice->permuter () == SYSTEM_DAWG_PERM) || + (word->best_choice->permuter () == FREQ_DAWG_PERM) || + (word->best_choice->permuter () == USER_DAWG_PERM) || + (word->best_choice->permuter () == NUMBER_PERM) || + (acceptable_word_string (str) != AC_UNACCEPTABLE) || ok_dict_word)) + return G_OK; + + ok_chars = len - bad_char_count - isolated_digits - + isolated_alphas - tess_rejs; + + if (crunch_debug > 3) { + tprintf ("garbage_word: \"%s\"\n", + word->best_choice->string ().string ()); + tprintf ("LEN: %d bad: %d iso_N: %d iso_A: %d rej: %d\n", + len, + bad_char_count, isolated_digits, isolated_alphas, tess_rejs); + } + if ((bad_char_count == 0) && + (tess_rejs == 0) && + ((len > isolated_digits + isolated_alphas) || (len <= 2))) + return G_OK; + + if ((tess_rejs > ok_chars) || + ((tess_rejs > 0) && ((bad_char_count + tess_rejs) * 2 > len))) + return G_TERRIBLE; + + if (len > 4) { + dodgy_chars = 2 * tess_rejs + bad_char_count + + isolated_digits + isolated_alphas; + if ((dodgy_chars > 5) || ((dodgy_chars / (float) len) > 0.5)) + return G_DODGY; + else + return G_OK; + } + else { + dodgy_chars = 2 * tess_rejs + bad_char_count; + if (((len == 4) && (dodgy_chars > 2)) || + ((len == 3) && (dodgy_chars > 2)) || (dodgy_chars >= len)) + return G_DODGY; + else + return G_OK; + } +} + + +/************************************************************************* + * word_deletable() + * DELETE WERDS AT ENDS OF ROWS IF + * Word is crunched && + * ( string length = 0 OR + * > 50% of chars are "|" (before merging) OR + * certainty < -10 OR + * rating /char > 60 OR + * TOP of word is more than 0.5 xht BELOW baseline OR + * BOTTOM of word is more than 0.5 xht ABOVE xht OR + * length of word < 3xht OR + * height of word < 0.7 xht OR + * height of word > 3.0 xht OR + * >75% of the outline BBs have longest dimension < 0.5xht + *************************************************************************/ + +CRUNCH_MODE word_deletable(WERD_RES *word, INT16 &delete_mode) { + int word_len = word->reject_map.length (); + float rating_per_ch; + BOX box; //BB of word + + if (word->unlv_crunch_mode == CR_NONE) { + delete_mode = 0; + return CR_NONE; + } + + if (word_len == 0) { + delete_mode = 1; + return CR_DELETE; + } + + box = word->outword->bounding_box (); + if (box.height () < crunch_del_min_ht * bln_x_height) { + delete_mode = 4; + return CR_DELETE; + } + + if (noise_outlines (word->outword)) { + delete_mode = 5; + return CR_DELETE; + } + + if ((failure_count (word) * 1.5) > word_len) { + delete_mode = 2; + return CR_LOOSE_SPACE; + } + + if (word->best_choice->certainty () < crunch_del_cert) { + delete_mode = 7; + return CR_LOOSE_SPACE; + } + + rating_per_ch = word->best_choice->rating () / word_len; + + if (rating_per_ch > crunch_del_rating) { + delete_mode = 8; + return CR_LOOSE_SPACE; + } + + if (box.top () < bln_baseline_offset - crunch_del_low_word * bln_x_height) { + delete_mode = 9; + return CR_LOOSE_SPACE; + } + + if (box.bottom () > + bln_baseline_offset + crunch_del_high_word * bln_x_height) { + delete_mode = 10; + return CR_LOOSE_SPACE; + } + + if (box.height () > crunch_del_max_ht * bln_x_height) { + delete_mode = 11; + return CR_LOOSE_SPACE; + } + + if (box.width () < crunch_del_min_width * bln_x_height) { + delete_mode = 3; + return CR_LOOSE_SPACE; + } + + delete_mode = 0; + return CR_NONE; +} + + +INT16 failure_count(WERD_RES *word) { + char *str = (char *) word->best_choice->string ().string (); + int tess_rejs = 0; + + for (; *str != '\0'; str++) { + if (*str == ' ') + tess_rejs++; + } + return tess_rejs; +} + + +BOOL8 noise_outlines(WERD *word) { + PBLOB_IT blob_it; + OUTLINE_IT outline_it; + BOX box; //BB of outline + INT16 outline_count = 0; + INT16 small_outline_count = 0; + INT16 max_dimension; + float small_limit = bln_x_height * crunch_small_outlines_size; + + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + outline_it.set_to_list (blob_it.data ()->out_list ()); + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); outline_it.forward ()) { + outline_count++; + box = outline_it.data ()->bounding_box (); + if (box.height () > box.width ()) + max_dimension = box.height (); + else + max_dimension = box.width (); + if (max_dimension < small_limit) + small_outline_count++; + } + } + return (small_outline_count >= outline_count); +} + + +/************************************************************************* + * insert_rej_cblobs() + * Put rejected word blobs back into the outword. + * NOTE!!! AFTER THIS THE CHOICES LIST WILL NOT HAVE THE CORRECT NUMBER + * OF ELEMENTS. + *************************************************************************/ +void insert_rej_cblobs( //word to do + WERD_RES *word) { + PBLOB_IT blob_it; //blob iterator + PBLOB_IT rej_blob_it; + const STRING *wordstr; + int old_len; + int rej_len; + char new_str[512]; + REJMAP new_map; + int i = 0; //new_str index + int j = 0; //old_str index + int new_len; + + gblob_sort_list (word->outword->rej_blob_list (), TRUE); + rej_blob_it.set_to_list (word->outword->rej_blob_list ()); + if (rej_blob_it.empty ()) + return; + rej_len = rej_blob_it.length (); + blob_it.set_to_list (word->outword->blob_list ()); + wordstr = &(word->best_choice->string ()); + old_len = wordstr->length (); + ASSERT_HOST (word->reject_map.length () == old_len); + ASSERT_HOST (blob_it.length () == old_len); + if ((old_len + rej_len) > 511) + return; //Word is garbage anyway prevent abort + new_map.initialise (old_len + rej_len); + + while (!rej_blob_it.empty ()) { + if ((j >= old_len) || + (rej_blob_it.data ()->bounding_box ().left () <= + blob_it.data ()->bounding_box ().left ())) { + /* Insert reject blob */ + if (j >= old_len) + blob_it.add_to_end (rej_blob_it.extract ()); + else + blob_it.add_before_stay_put (rej_blob_it.extract ()); + if (!rej_blob_it.empty ()) + rej_blob_it.forward (); + new_str[i] = ' '; + new_map[i].setrej_rej_cblob (); + i++; + } + else { + new_str[i] = (*wordstr)[j]; + new_map[i] = word->reject_map[j]; + i++; + j++; + blob_it.forward (); + } + } + /* Add any extra normal blobs to strings */ + while (j < wordstr->length ()) { + new_str[i] = (*wordstr)[j]; + new_map[i] = word->reject_map[j]; + i++; + j++; + } + new_str[i] = '\0'; + /* + tprintf( + "\nOld len %d; New len %d; New str \"%s\"; New map \"%s\"\n", + old_len, i, new_str, new_map ); + */ + ASSERT_HOST (i == blob_it.length ()); + ASSERT_HOST (i == old_len + rej_len); + word->reject_map = new_map; + *((STRING *) wordstr) = new_str; + new_len = strlen (word->best_choice->string ().string ()); + ASSERT_HOST (word->reject_map.length () == new_len); + ASSERT_HOST (word->outword->blob_list ()->length () == new_len); +} diff --git a/ccmain/docqual.h b/ccmain/docqual.h new file mode 100644 index 0000000000..f0d340619c --- /dev/null +++ b/ccmain/docqual.h @@ -0,0 +1,155 @@ +/****************************************************************** + * File: docqual.h (Formerly docqual.h) + * Description: Document Quality Metrics + * Author: Phil Cheatle + * Created: Mon May 9 11:27:28 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef DOCQUAL_H +#define DOCQUAL_H + +#include "control.h" +#include "notdll.h" + +enum GARBAGE_LEVEL +{ + G_NEVER_CRUNCH, + G_OK, + G_DODGY, + G_TERRIBLE +}; + +extern STRING_VAR_H (outlines_odd, "%| ", "Non standard number of outlines"); +extern STRING_VAR_H (outlines_2, "ij!?%\":;", +"Non standard number of outlines"); +extern BOOL_VAR_H (docqual_excuse_outline_errs, FALSE, +"Allow outline errs in unrejection?"); +extern BOOL_VAR_H (tessedit_good_quality_unrej, TRUE, +"Reduce rejection on good docs"); +extern BOOL_VAR_H (tessedit_use_reject_spaces, TRUE, "Reject spaces?"); +extern double_VAR_H (tessedit_reject_doc_percent, 65.00, +"%rej allowed before rej whole doc"); +extern double_VAR_H (tessedit_reject_block_percent, 45.00, +"%rej allowed before rej whole block"); +extern double_VAR_H (tessedit_reject_row_percent, 40.00, +"%rej allowed before rej whole row"); +extern double_VAR_H (tessedit_whole_wd_rej_row_percent, 70.00, +"%of row rejects in whole word rejects which prevents whole row rejection"); +extern BOOL_VAR_H (tessedit_preserve_blk_rej_perfect_wds, TRUE, +"Only rej partially rejected words in block rejection"); +extern BOOL_VAR_H (tessedit_preserve_row_rej_perfect_wds, TRUE, +"Only rej partially rejected words in row rejection"); +extern BOOL_VAR_H (tessedit_dont_blkrej_good_wds, FALSE, +"Use word segmentation quality metric"); +extern BOOL_VAR_H (tessedit_dont_rowrej_good_wds, FALSE, +"Use word segmentation quality metric"); +extern INT_VAR_H (tessedit_preserve_min_wd_len, 2, +"Only preserve wds longer than this"); +extern BOOL_VAR_H (tessedit_row_rej_good_docs, TRUE, +"Apply row rejection to good docs"); +extern double_VAR_H (tessedit_good_doc_still_rowrej_wd, 1.1, +"rej good doc wd if more than this fraction rejected"); +extern BOOL_VAR_H (tessedit_reject_bad_qual_wds, TRUE, +"Reject all bad quality wds"); +extern BOOL_VAR_H (tessedit_debug_doc_rejection, FALSE, "Page stats"); +extern BOOL_VAR_H (tessedit_debug_quality_metrics, FALSE, +"Output data to debug file"); +extern BOOL_VAR_H (bland_unrej, FALSE, "unrej potential with no chekcs"); +extern double_VAR_H (quality_rowrej_pc, 1.1, +"good_quality_doc gte good char limit"); +extern BOOL_VAR_H (unlv_tilde_crunching, TRUE, +"Mark v.bad words for tilde crunch"); +extern BOOL_VAR_H (crunch_early_merge_tess_fails, TRUE, +"Before word crunch?"); +extern BOOL_VAR_H (crunch_early_convert_bad_unlv_chs, FALSE, +"Take out ~^ early?"); +extern double_VAR_H (crunch_terrible_rating, 80.0, "crunch rating lt this"); +extern BOOL_VAR_H (crunch_terrible_garbage, TRUE, "As it says"); +extern double_VAR_H (crunch_poor_garbage_cert, -9.0, +"crunch garbage cert lt this"); +extern double_VAR_H (crunch_poor_garbage_rate, 60, +"crunch garbage rating lt this"); +extern double_VAR_H (crunch_pot_poor_rate, 40, +"POTENTIAL crunch rating lt this"); +extern double_VAR_H (crunch_pot_poor_cert, -8.0, +"POTENTIAL crunch cert lt this"); +extern BOOL_VAR_H (crunch_pot_garbage, TRUE, "POTENTIAL crunch garbage"); +extern double_VAR_H (crunch_del_rating, 60, +"POTENTIAL crunch rating lt this"); +extern double_VAR_H (crunch_del_cert, -10.0, "POTENTIAL crunch cert lt this"); +extern double_VAR_H (crunch_del_min_ht, 0.7, "Del if word ht lt xht x this"); +extern double_VAR_H (crunch_del_max_ht, 3.0, "Del if word ht gt xht x this"); +extern double_VAR_H (crunch_del_min_width, 3.0, +"Del if word width lt xht x this"); +extern double_VAR_H (crunch_del_high_word, 1.5, +"Del if word gt xht x this above bl"); +extern double_VAR_H (crunch_del_low_word, 0.5, +"Del if word gt xht x this below bl"); +extern double_VAR_H (crunch_small_outlines_size, 0.6, +"Small if lt xht x this"); +extern INT_VAR_H (crunch_rating_max, 10, "For adj length in rating per ch"); +extern INT_VAR_H (crunch_pot_indicators, 1, +"How many potential indicators needed"); +extern BOOL_VAR_H (crunch_leave_ok_strings, TRUE, +"Dont touch sensible strings"); +extern BOOL_VAR_H (crunch_accept_ok, TRUE, "Use acceptability in okstring"); +extern BOOL_VAR_H (crunch_leave_accept_strings, FALSE, +"Dont pot crunch sensible strings"); +extern BOOL_VAR_H (crunch_include_numerals, FALSE, "Fiddle alpha figures"); +extern INT_VAR_H (crunch_leave_lc_strings, 4, +"Dont crunch words with long lower case strings"); +extern INT_VAR_H (crunch_leave_uc_strings, 4, +"Dont crunch words with long lower case strings"); +extern INT_VAR_H (crunch_long_repetitions, 3, +"Crunch words with long repetitions"); +extern INT_VAR_H (crunch_debug, 0, "As it says"); +INT16 word_blob_quality( //Blob seg changes + WERD_RES *word, + ROW *row); +BOOL8 crude_match_blobs(PBLOB *blob1, PBLOB *blob2); +INT16 word_outline_errs( //Outline count errs + WERD_RES *word); +void word_char_quality( //Blob seg changes + WERD_RES *word, + ROW *row, + INT16 *match_count, + INT16 *accepted_match_count); +void unrej_good_chs(WERD_RES *word, ROW *row); +void print_boxes(WERD *word); +INT16 count_outline_errs(char c, INT16 outline_count); +void quality_based_rejection(PAGE_RES_IT &page_res_it, BOOL8 good_quality_doc); +void unrej_good_quality_words( //unreject potential + PAGE_RES_IT &page_res_it); +void doc_and_block_rejection( //reject big chunks + PAGE_RES_IT &page_res_it, + BOOL8 good_quality_doc); +void reject_whole_page(PAGE_RES_IT &page_res_it); +void tilde_crunch(PAGE_RES_IT &page_res_it); +BOOL8 terrible_word_crunch(WERD_RES *word, GARBAGE_LEVEL garbage_level); +BOOL8 potential_word_crunch(WERD_RES *word, + GARBAGE_LEVEL garbage_level, + BOOL8 ok_dict_word); +void tilde_delete(PAGE_RES_IT &page_res_it); + //word to do +void convert_bad_unlv_chs(WERD_RES *word_res); + //word to do +void merge_tess_fails(WERD_RES *word_res); +GARBAGE_LEVEL garbage_word(WERD_RES *word, BOOL8 ok_dict_word); +CRUNCH_MODE word_deletable(WERD_RES *word, INT16 &delete_mode); +INT16 failure_count(WERD_RES *word); +BOOL8 noise_outlines(WERD *word); + //word to do +void insert_rej_cblobs(WERD_RES *word); +#endif diff --git a/ccmain/expandblob.cpp b/ccmain/expandblob.cpp new file mode 100644 index 0000000000..f80236a89d --- /dev/null +++ b/ccmain/expandblob.cpp @@ -0,0 +1,82 @@ +/************************************************************************** + * Revision 5.1 89/07/27 11:46:53 11:46:53 ray () + * (C) Copyright 1989, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * +**************************************************************************/ +#include "mfcpch.h" +#include "expandblob.h" +#include "tessclas.h" +#include "const.h" +#include "structures.h" +#include "freelist.h" + +/*********************************************************************** +free_blob(blob) frees the blob and everything it is connected to, +i.e. outlines, nodes, edgepts, bytevecs, ratings etc +*************************************************************************/ +void free_blob( /*blob to free */ + register TBLOB *blob) { + if (blob == NULL) + return; /*duff blob */ + free_tree (blob->outlines); /*do the tree of outlines */ + oldblob(blob); /*free the actual blob */ +} + + +/*************************************************************************** +free_tree(outline) frees the current outline +and then its sub-tree +*****************************************************************************/ +void free_tree( /*outline to draw */ + register TESSLINE *outline) { + if (outline == NULL) + return; /*duff outline */ + if (outline->next != NULL) + free_tree (outline->next); + if (outline->child != NULL) + free_tree (outline->child); /*and sub-tree */ + free_outline(outline); /*free the outline */ +} + + +/******************************************************************************* +free_outline(outline) frees an outline and anything connected to it +*********************************************************************************/ +void free_outline( /*outline to free */ + register TESSLINE *outline) { + if (outline->compactloop != NULL) + /*no compact loop */ + memfree (outline->compactloop); + + if (outline->loop != NULL) + free_loop (outline->loop); + + oldoutline(outline); +} + + +/********************************************************************************* +free_loop(startpt) frees all the elements of the closed loop +starting at startpt +***********************************************************************************/ +void free_loop( /*outline to free */ + register EDGEPT *startpt) { + register EDGEPT *edgept; /*current point */ + + if (startpt == NULL) + return; + edgept = startpt; + do { + edgept = oldedgept (edgept); /*free it and move on */ + } + while (edgept != startpt); +} diff --git a/ccmain/expandblob.h b/ccmain/expandblob.h new file mode 100644 index 0000000000..6d80c288db --- /dev/null +++ b/ccmain/expandblob.h @@ -0,0 +1,13 @@ +#ifndef EXPANDBLOB_H +#define EXPANDBLOB_H + +#include "tessclas.h" + +void free_blob(register TBLOB *blob); + +void free_tree(register TESSLINE *outline); + +void free_outline(register TESSLINE *outline); + +void free_loop(register EDGEPT *startpt); +#endif diff --git a/ccmain/fixspace.cpp b/ccmain/fixspace.cpp new file mode 100644 index 0000000000..c3e8439a30 --- /dev/null +++ b/ccmain/fixspace.cpp @@ -0,0 +1,974 @@ +/****************************************************************** + * File: fixspace.cpp (Formerly fixspace.c) + * Description: Implements a pass over the page res, exploring the alternative + * spacing possibilities, trying to use context to improve the + word spacing +* Author: Phil Cheatle +* Created: Thu Oct 21 11:38:43 BST 1993 +* +* (C) Copyright 1993, Hewlett-Packard Ltd. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +* +**********************************************************************/ + +#include "mfcpch.h" +#include +#include "reject.h" +#include "statistc.h" +#include "genblob.h" +#include "control.h" +#include "fixspace.h" +#include "tessvars.h" +#include "tessbox.h" +#include "secname.h" + +#define EXTERN + +EXTERN BOOL_VAR (fixsp_check_for_fp_noise_space, TRUE, +"Try turning noise to space in fixed pitch"); +EXTERN BOOL_VAR (fixsp_fp_eval, TRUE, "Use alternate evaluation for fp"); +EXTERN BOOL_VAR (fixsp_noise_score_fixing, TRUE, "More sophisticated?"); +EXTERN INT_VAR (fixsp_non_noise_limit, 1, +"How many non-noise blbs either side?"); +EXTERN double_VAR (fixsp_small_outlines_size, 0.28, "Small if lt xht x this"); + +EXTERN BOOL_VAR (fixsp_ignore_punct, TRUE, "In uniform spacing calc"); +EXTERN BOOL_VAR (fixsp_numeric_fix, TRUE, "Try to deal with numeric punct"); +EXTERN BOOL_VAR (fixsp_prefer_joined_1s, TRUE, "Arbitrary boost"); +EXTERN BOOL_VAR (tessedit_test_uniform_wd_spacing, FALSE, +"Limit context word spacing"); +EXTERN BOOL_VAR (tessedit_prefer_joined_punct, FALSE, +"Reward punctation joins"); +EXTERN INT_VAR (fixsp_done_mode, 1, "What constitues done for spacing"); +EXTERN INT_VAR (debug_fix_space_level, 0, "Contextual fixspace debug"); +EXTERN STRING_VAR (numeric_punctuation, ".,", +"Punct. chs expected WITHIN numbers"); + +#define PERFECT_WERDS 999 +#define MAXSPACING 128 /*max expected spacing in pix */ + +/************************************************************************* + * fix_fuzzy_spaces() + * Walk over the page finding sequences of words joined by fuzzy spaces. Extract + * them as a sublist, process the sublist to find the optimal arrangement of + * spaces then replace the sublist in the ROW_RES. + *************************************************************************/ + +void fix_fuzzy_spaces( //find fuzzy words + volatile ETEXT_DESC *monitor, //progress monitor + INT32 word_count, //count of words in doc + PAGE_RES *page_res) { + BLOCK_RES_IT block_res_it; //iterators + ROW_RES_IT row_res_it; + WERD_RES_IT word_res_it_from; + WERD_RES_IT word_res_it_to; + WERD_RES *word_res; + WERD_RES_LIST fuzzy_space_words; + INT16 new_length; + BOOL8 prevent_null_wd_fixsp; //DONT process blobless wds + INT32 word_index; //current word + + block_res_it.set_to_list (&page_res->block_res_list); + word_index = 0; + for (block_res_it.mark_cycle_pt (); + !block_res_it.cycled_list (); block_res_it.forward ()) { + row_res_it.set_to_list (&block_res_it.data ()->row_res_list); + for (row_res_it.mark_cycle_pt (); + !row_res_it.cycled_list (); row_res_it.forward ()) { + word_res_it_from.set_to_list (&row_res_it.data ()->word_res_list); + while (!word_res_it_from.at_last ()) { + word_res = word_res_it_from.data (); + while (!word_res_it_from.at_last () && + !(word_res->combination || + word_res_it_from.data_relative (1)-> + word->flag (W_FUZZY_NON) || + word_res_it_from.data_relative (1)-> + word->flag (W_FUZZY_SP))) { + fix_sp_fp_word (word_res_it_from, row_res_it.data ()->row); + word_res = word_res_it_from.forward (); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 90 + 5 * word_index / word_count; + } + } + + if (!word_res_it_from.at_last ()) { + word_res_it_to = word_res_it_from; + prevent_null_wd_fixsp = + word_res->word->gblob_list ()->empty (); + if (check_debug_pt (word_res, 60)) + debug_fix_space_level.set_value (10); + word_res_it_to.forward (); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 90 + 5 * word_index / word_count; + } + while (!word_res_it_to.at_last () && + (word_res_it_to.data_relative (1)-> + word->flag (W_FUZZY_NON) || + word_res_it_to.data_relative (1)-> + word->flag (W_FUZZY_SP))) { + if (check_debug_pt (word_res, 60)) + debug_fix_space_level.set_value (10); + if (word_res->word->gblob_list ()->empty ()) + prevent_null_wd_fixsp = TRUE; + word_res = word_res_it_to.forward (); + } + if (check_debug_pt (word_res, 60)) + debug_fix_space_level.set_value (10); + if (word_res->word->gblob_list ()->empty ()) + prevent_null_wd_fixsp = TRUE; + if (prevent_null_wd_fixsp) + word_res_it_from = word_res_it_to; + else { + fuzzy_space_words.assign_to_sublist (&word_res_it_from, + &word_res_it_to); + fix_fuzzy_space_list (fuzzy_space_words, + row_res_it.data ()->row); + new_length = fuzzy_space_words.length (); + word_res_it_from.add_list_before (&fuzzy_space_words); + for (; + (!word_res_it_from.at_last () && + (new_length > 0)); new_length--) { + word_res_it_from.forward (); + } + } + if (test_pt) + debug_fix_space_level.set_value (0); + } + fix_sp_fp_word (word_res_it_from, row_res_it.data ()->row); + //Last word in row + } + } + } +} + + +void fix_fuzzy_space_list( //space explorer + WERD_RES_LIST &best_perm, + ROW *row) { + INT16 best_score; + WERD_RES_LIST current_perm; + INT16 current_score; + BOOL8 improved = FALSE; + + //default score + best_score = eval_word_spacing (best_perm); + + dump_words (best_perm, best_score, 1, improved); + + if (best_score != PERFECT_WERDS) + initialise_search(best_perm, current_perm); + + while ((best_score != PERFECT_WERDS) && !current_perm.empty ()) { + match_current_words(current_perm, row); + current_score = eval_word_spacing (current_perm); + dump_words (current_perm, current_score, 2, improved); + if (current_score > best_score) { + best_perm.clear (); + best_perm.deep_copy (¤t_perm); + best_score = current_score; + improved = TRUE; + } + if (current_score < PERFECT_WERDS) + transform_to_next_perm(current_perm); + } + dump_words (best_perm, best_score, 3, improved); +} + + +void initialise_search(WERD_RES_LIST &src_list, WERD_RES_LIST &new_list) { + WERD_RES_IT src_it(&src_list); + WERD_RES_IT new_it(&new_list); + WERD_RES *src_wd; + WERD_RES *new_wd; + + for (src_it.mark_cycle_pt (); !src_it.cycled_list (); src_it.forward ()) { + src_wd = src_it.data (); + if (!src_wd->combination) { + new_wd = new WERD_RES (*src_wd); + new_wd->combination = FALSE; + new_wd->part_of_combo = FALSE; + new_it.add_after_then_move (new_wd); + } + } +} + + +void match_current_words(WERD_RES_LIST &words, ROW *row) { + WERD_RES_IT word_it(&words); + WERD_RES *word; + + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if ((!word->part_of_combo) && (word->outword == NULL)) + classify_word_pass2(word, row); + } +} + + +/************************************************************************* + * eval_word_spacing() + * The basic measure is the number of characters in contextually confirmed + * words. (I.e the word is done) + * If all words are contextually confirmed the evaluation is deemed perfect. + * + * Some fiddles are done to handle "1"s as these are VERY frequent causes of + * fuzzy spaces. The problem with the basic measure is that "561 63" would score + * the same as "56163", though given our knowledge that the space is fuzzy, and + * that there is a "1" next to the fuzzy space, we need to ensure that "56163" + * is prefered. + * + * The solution is to NOT COUNT the score of any word which has a digit at one + * end and a "1Il" as the character the other side of the space. + * + * Conversly, any character next to a "1" within a word is counted as a positive + * score. Thus "561 63" would score 4 (3 chars in a numeric word plus 1 side of + * the "1" joined). "56163" would score 7 - all chars in a numeric word + 2 + * sides of a "1" joined. + * + * The joined 1 rule is applied to any word REGARDLESS of contextual + * confirmation. Thus "PS7a71 3/7a" scores 1 (neither word is contexutally + * confirmed. The only score is from the joined 1. "PS7a713/7a" scores 2. + * + *************************************************************************/ +INT16 eval_word_spacing(WERD_RES_LIST &word_res_list) { + WERD_RES_IT word_res_it(&word_res_list); + INT16 total_score = 0; + INT16 word_count = 0; + INT16 done_word_count = 0; + INT16 word_len; + INT16 i; + WERD_RES *word; //current word + INT16 prev_word_score = 0; + BOOL8 prev_word_done = FALSE; + BOOL8 prev_char_1 = FALSE; //prev ch a "1/I/l"? + BOOL8 prev_char_digit = FALSE; //prev ch 2..9 or 0 + BOOL8 current_char_1 = FALSE; + BOOL8 current_word_ok_so_far; + STRING punct_chars = "!\"`',.:;"; + BOOL8 prev_char_punct = FALSE; + BOOL8 current_char_punct = FALSE; + BOOL8 word_done = FALSE; + + do { + word = word_res_it.data (); + word_done = fixspace_thinks_word_done (word); + word_count++; + if (word->tess_failed) { + total_score += prev_word_score; + if (prev_word_done) + done_word_count++; + prev_word_score = 0; + prev_char_1 = FALSE; + prev_char_digit = FALSE; + prev_word_done = FALSE; + } + else { + /* + Can we add the prev word score and potentially count this word? + Yes IF it didnt end in a 1 when the first char of this word is a digit + AND it didnt end in a digit when the first char of this word is a 1 + */ + word_len = word->reject_map.length (); + current_word_ok_so_far = FALSE; + if (!((prev_char_1 && + digit_or_numeric_punct (word, + word->best_choice->string ()[0])) || + (prev_char_digit && + ((word_done && + (word->best_choice->string ()[0] == '1')) || + (!word_done && + STRING (conflict_set_I_l_1).contains (word->best_choice-> + string ()[0])))))) { + total_score += prev_word_score; + if (prev_word_done) + done_word_count++; + current_word_ok_so_far = word_done; + } + + if ((current_word_ok_so_far) && + (!tessedit_test_uniform_wd_spacing || + ((word->best_choice->permuter () == NUMBER_PERM) || + uniformly_spaced (word)))) { + prev_word_done = TRUE; + prev_word_score = word_len; + } + else { + prev_word_done = FALSE; + prev_word_score = 0; + } + + if (fixsp_prefer_joined_1s) { + /* Add 1 to total score for every joined 1 regardless of context and rejtn */ + + for (i = 0, prev_char_1 = FALSE; i < word_len; i++) { + current_char_1 = word->best_choice->string ()[i] == '1'; + if (prev_char_1 || (current_char_1 && (i > 0))) + total_score++; + prev_char_1 = current_char_1; + } + } + + /* Add 1 to total score for every joined punctuation regardless of context + and rejtn */ + if (tessedit_prefer_joined_punct) { + for (i = 0, prev_char_punct = FALSE; i < word_len; i++) { + current_char_punct = + punct_chars.contains (word->best_choice->string ()[i]); + if (prev_char_punct || (current_char_punct && (i > 0))) + total_score++; + prev_char_punct = current_char_punct; + } + } + prev_char_digit = digit_or_numeric_punct (word, + word->best_choice-> + string ()[word_len - 1]); + prev_char_1 = + ((word_done + && (word->best_choice->string ()[word_len - 1] == '1')) + || (!word_done + && STRING (conflict_set_I_l_1).contains (word->best_choice-> + string ()[word_len - + 1]))); + } + /* Find next word */ + do + word_res_it.forward (); + while (word_res_it.data ()->part_of_combo); + } + while (!word_res_it.at_first ()); + total_score += prev_word_score; + if (prev_word_done) + done_word_count++; + if (done_word_count == word_count) + return PERFECT_WERDS; + else + return total_score; +} + + +BOOL8 digit_or_numeric_punct(WERD_RES *word, char ch) { + return (isdigit (ch) || + (fixsp_numeric_fix && + (word->best_choice->permuter () == NUMBER_PERM) && + STRING (numeric_punctuation).contains (ch))); +} + + +/************************************************************************* + * transform_to_next_perm() + * Examines the current word list to find the smallest word gap size. Then walks + * the word list closing any gaps of this size by either inserted new + * combination words, or extending existing ones. + * + * The routine COULD be limited to stop it building words longer than N blobs. + * + * If there are no more gaps then it DELETES the entire list and returns the + * empty list to cause termination. + *************************************************************************/ +void transform_to_next_perm(WERD_RES_LIST &words) { + WERD_RES_IT word_it(&words); + WERD_RES_IT prev_word_it(&words); + WERD_RES *word; + WERD_RES *prev_word; + WERD_RES *combo; + WERD *copy_word; + INT16 prev_right = -1; + BOX box; + INT16 gap; + INT16 min_gap = MAX_INT16; + + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (!word->part_of_combo) { + box = word->word->bounding_box (); + if (prev_right >= 0) { + gap = box.left () - prev_right; + if (gap < min_gap) + min_gap = gap; + } + prev_right = box.right (); + } + } + if (min_gap < MAX_INT16) { + prev_right = -1; //back to start + word_it.set_to_list (&words); + for (; //cant use cycle pt due to inserted combos at start of list + (prev_right < 0) || !word_it.at_first (); word_it.forward ()) { + word = word_it.data (); + if (!word->part_of_combo) { + box = word->word->bounding_box (); + if (prev_right >= 0) { + gap = box.left () - prev_right; + if (gap <= min_gap) { + prev_word = prev_word_it.data (); + if (prev_word->combination) + combo = prev_word; + else { + /* Make a new combination and insert before the first word being joined */ + copy_word = new WERD; + *copy_word = *(prev_word->word); + //deep copy + combo = new WERD_RES (copy_word); + combo->combination = TRUE; + prev_word->part_of_combo = TRUE; + prev_word_it.add_before_then_move (combo); + } + combo->word->set_flag (W_EOL, word->word->flag (W_EOL)); + if (word->combination) { + combo->word->join_on (word->word); + //Move blbs to combo + //old combo no longer needed + delete word_it.extract (); + } + else { + //Cpy current wd to combo + combo->copy_on (word); + word->part_of_combo = TRUE; + } + combo->done = FALSE; + if (combo->outword != NULL) { + delete combo->outword; + delete combo->best_choice; + delete combo->raw_choice; + combo->outword = NULL; + combo->best_choice = NULL; + combo->raw_choice = NULL; + } + } + else + //catch up + prev_word_it = word_it; + } + prev_right = box.right (); + } + } + } + else + words.clear (); //signal termination +} + + +void dump_words(WERD_RES_LIST &perm, INT16 score, INT16 mode, BOOL8 improved) { + WERD_RES_IT word_res_it(&perm); + static STRING initial_str; + + if (debug_fix_space_level > 0) { + if (mode == 1) { + initial_str = ""; + for (word_res_it.mark_cycle_pt (); + !word_res_it.cycled_list (); word_res_it.forward ()) { + if (!word_res_it.data ()->part_of_combo) { + initial_str += word_res_it.data ()->best_choice->string (); + initial_str += ' '; + } + } + } + + #ifndef SECURE_NAMES + if (debug_fix_space_level > 1) { + switch (mode) { + case 1: + tprintf ("EXTRACTED (%d): \"", score); + break; + case 2: + tprintf ("TESTED (%d): \"", score); + break; + case 3: + tprintf ("RETURNED (%d): \"", score); + break; + } + + for (word_res_it.mark_cycle_pt (); + !word_res_it.cycled_list (); word_res_it.forward ()) { + if (!word_res_it.data ()->part_of_combo) + tprintf ("%s/%1d ", + word_res_it.data ()->best_choice->string (). + string (), + (int) word_res_it.data ()->best_choice->permuter ()); + } + tprintf ("\"\n"); + } + else if (improved) { + tprintf ("FIX SPACING \"%s\" => \"", initial_str.string ()); + for (word_res_it.mark_cycle_pt (); + !word_res_it.cycled_list (); word_res_it.forward ()) { + if (!word_res_it.data ()->part_of_combo) + tprintf ("%s/%1d ", + word_res_it.data ()->best_choice->string (). + string (), + (int) word_res_it.data ()->best_choice->permuter ()); + } + tprintf ("\"\n"); + } + #endif + } +} + + +/************************************************************************* + * uniformly_spaced() + * Return true if one of the following are true: + * - All inter-char gaps are the same width + * - The largest gap is no larger than twice the mean/median of the others + * - The largest gap is < 64/5 = 13 and all others are <= 0 + * **** REMEMBER - WE'RE NOW WORKING WITH A BLN WERD !!! + *************************************************************************/ +BOOL8 uniformly_spaced( //sensible word + WERD_RES *word) { + PBLOB_IT blob_it; + BOX box; + INT16 prev_right = -MAX_INT16; + INT16 gap; + INT16 max_gap = -MAX_INT16; + INT16 max_gap_count = 0; + STATS gap_stats (0, MAXSPACING); + BOOL8 result; + const ROW *row = word->denorm.row (); + float max_non_space; + float normalised_max_nonspace; + INT16 i = 0; + STRING punct_chars = "\"`',.:;"; + + blob_it.set_to_list (word->outword->blob_list ()); + + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + box = blob_it.data ()->bounding_box (); + if ((prev_right > -MAX_INT16) && + (!fixsp_ignore_punct || + (!punct_chars.contains (word->best_choice->string ()[i - 1]) && + !punct_chars.contains (word->best_choice->string ()[i])))) { + gap = box.left () - prev_right; + if (gap < max_gap) + gap_stats.add (gap, 1); + else if (gap == max_gap) + max_gap_count++; + else { + if (max_gap_count > 0) + gap_stats.add (max_gap, max_gap_count); + max_gap = gap; + max_gap_count = 1; + } + } + prev_right = box.right (); + i++; + } + + max_non_space = (row->space () + 3 * row->kern ()) / 4; + normalised_max_nonspace = max_non_space * bln_x_height / row->x_height (); + + result = ((gap_stats.get_total () == 0) || + (max_gap <= normalised_max_nonspace) || + ((gap_stats.get_total () > 2) && + (max_gap <= 2 * gap_stats.median ())) || + ((gap_stats.get_total () <= 2) && + (max_gap <= 2 * gap_stats.mean ()))); + #ifndef SECURE_NAMES + if ((debug_fix_space_level > 1)) { + if (result) + tprintf + ("ACCEPT SPACING FOR: \"%s\" norm_maxnon = %f max=%d maxcount=%d total=%d mean=%f median=%f\n", + word->best_choice->string ().string (), normalised_max_nonspace, + max_gap, max_gap_count, gap_stats.get_total (), gap_stats.mean (), + gap_stats.median ()); + else + tprintf + ("REJECT SPACING FOR: \"%s\" norm_maxnon = %f max=%d maxcount=%d total=%d mean=%f median=%f\n", + word->best_choice->string ().string (), normalised_max_nonspace, + max_gap, max_gap_count, gap_stats.get_total (), gap_stats.mean (), + gap_stats.median ()); + } + #endif + + return result; +} + + +BOOL8 fixspace_thinks_word_done(WERD_RES *word) { + if (word->done) + return TRUE; + + /* + Use all the standard pass 2 conditions for mode 5 in set_done() in + reject.c BUT DONT REJECT IF THE WERD IS AMBIGUOUS - FOR SPACING WE DONT + CARE WHETHER WE HAVE of/at on/an etc. + */ + if ((fixsp_done_mode > 0) && + (word->tess_accepted || + ((fixsp_done_mode == 2) && + (word->reject_map.reject_count () == 0)) || + (fixsp_done_mode == 3)) && + (strchr (word->best_choice->string ().string (), ' ') == NULL) && + ((word->best_choice->permuter () == SYSTEM_DAWG_PERM) || + (word->best_choice->permuter () == FREQ_DAWG_PERM) || + (word->best_choice->permuter () == USER_DAWG_PERM) || + (word->best_choice->permuter () == NUMBER_PERM))) + return TRUE; + else + return FALSE; +} + + +/************************************************************************* + * fix_sp_fp_word() + * Test the current word to see if it can be split by deleting noise blobs. If + * so, do the buisiness. + * Return with the iterator pointing to the same place if the word is unchanged, + * or the last of the replacement words. + *************************************************************************/ +void fix_sp_fp_word(WERD_RES_IT &word_res_it, ROW *row) { + WERD_RES *word_res; + WERD_RES_LIST sub_word_list; + WERD_RES_IT sub_word_list_it(&sub_word_list); + INT16 blob_index; + INT16 new_length; + float junk; + + word_res = word_res_it.data (); + if (!fixsp_check_for_fp_noise_space || + word_res->word->flag (W_REP_CHAR) || + word_res->combination || + word_res->part_of_combo || !word_res->word->flag (W_DONT_CHOP)) + return; + + blob_index = worst_noise_blob (word_res, &junk); + if (blob_index < 0) + return; + + #ifndef SECURE_NAMES + if (debug_fix_space_level > 1) { + tprintf ("FP fixspace working on \"%s\"\n", + word_res->best_choice->string ().string ()); + } + #endif + gblob_sort_list ((PBLOB_LIST *) word_res->word->rej_cblob_list (), FALSE); + sub_word_list_it.add_after_stay_put (word_res_it.extract ()); + fix_noisy_space_list(sub_word_list, row); + new_length = sub_word_list.length (); + word_res_it.add_list_before (&sub_word_list); + for (; (!word_res_it.at_last () && (new_length > 1)); new_length--) { + word_res_it.forward (); + } +} + + +void fix_noisy_space_list(WERD_RES_LIST &best_perm, ROW *row) { + INT16 best_score; + WERD_RES_IT best_perm_it(&best_perm); + WERD_RES_LIST current_perm; + WERD_RES_IT current_perm_it(¤t_perm); + WERD_RES *old_word_res; + WERD_RES *new_word_res; + INT16 current_score; + BOOL8 improved = FALSE; + + //default score + best_score = fp_eval_word_spacing (best_perm); + + dump_words (best_perm, best_score, 1, improved); + + new_word_res = new WERD_RES; + old_word_res = best_perm_it.data (); + //Kludge to force deep copy + old_word_res->combination = TRUE; + *new_word_res = *old_word_res; //deep copy + //Undo kludge + old_word_res->combination = FALSE; + //Undo kludge + new_word_res->combination = FALSE; + current_perm_it.add_to_end (new_word_res); + + break_noisiest_blob_word(current_perm); + + while ((best_score != PERFECT_WERDS) && !current_perm.empty ()) { + match_current_words(current_perm, row); + current_score = fp_eval_word_spacing (current_perm); + dump_words (current_perm, current_score, 2, improved); + if (current_score > best_score) { + best_perm.clear (); + best_perm.deep_copy (¤t_perm); + best_score = current_score; + improved = TRUE; + } + if (current_score < PERFECT_WERDS) + break_noisiest_blob_word(current_perm); + } + dump_words (best_perm, best_score, 3, improved); +} + + +/************************************************************************* + * break_noisiest_blob_word() + * Find the word with the blob which looks like the worst noise. + * Break the word into two, deleting the noise blob. + *************************************************************************/ +void break_noisiest_blob_word(WERD_RES_LIST &words) { + WERD_RES_IT word_it(&words); + WERD_RES_IT worst_word_it; + float worst_noise_score = 9999; + int worst_blob_index = -1; //noisiest blb of noisiest wd + int blob_index; //of wds noisiest blb + float noise_score; //of wds noisiest blb + WERD_RES *word_res; + C_BLOB_IT blob_it; + C_BLOB_IT rej_cblob_it; + C_BLOB_LIST new_blob_list; + C_BLOB_IT new_blob_it; + C_BLOB_IT new_rej_cblob_it; + WERD *new_word; + INT16 start_of_noise_blob; + INT16 i; + + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + blob_index = worst_noise_blob (word_it.data (), &noise_score); + if ((blob_index > -1) && (worst_noise_score > noise_score)) { + worst_noise_score = noise_score; + worst_blob_index = blob_index; + worst_word_it = word_it; + } + } + if (worst_blob_index < 0) { + words.clear (); //signal termination + return; + } + + /* Now split the worst_word_it */ + + word_res = worst_word_it.data (); + + /* Move blobs before noise blob to a new bloblist */ + + new_blob_it.set_to_list (&new_blob_list); + blob_it.set_to_list (word_res->word->cblob_list ()); + for (i = 0; i < worst_blob_index; i++, blob_it.forward ()) { + new_blob_it.add_after_then_move (blob_it.extract ()); + } + start_of_noise_blob = blob_it.data ()->bounding_box ().left (); + delete blob_it.extract (); //throw out noise blb + + new_word = new WERD (&new_blob_list, word_res->word); + new_word->set_flag (W_EOL, FALSE); + word_res->word->set_flag (W_BOL, FALSE); + word_res->word->set_blanks (1);//After break + + new_rej_cblob_it.set_to_list (new_word->rej_cblob_list ()); + rej_cblob_it.set_to_list (word_res->word->rej_cblob_list ()); + for (; + (!rej_cblob_it.empty () && + (rej_cblob_it.data ()->bounding_box ().left () < + start_of_noise_blob)); rej_cblob_it.forward ()) { + new_rej_cblob_it.add_after_then_move (rej_cblob_it.extract ()); + } + + worst_word_it.add_before_then_move (new WERD_RES (new_word)); + + word_res->done = FALSE; + if (word_res->outword != NULL) { + delete word_res->outword; + delete word_res->best_choice; + delete word_res->raw_choice; + word_res->outword = NULL; + word_res->best_choice = NULL; + word_res->raw_choice = NULL; + } +} + + +INT16 worst_noise_blob(WERD_RES *word_res, float *worst_noise_score) { + PBLOB_IT blob_it; + INT16 blob_count; + float noise_score[512]; + int i; + int min_noise_blob; //1st contender + int max_noise_blob; //last contender + int non_noise_count; + int worst_noise_blob; //Worst blob + float small_limit = bln_x_height * fixsp_small_outlines_size; + float non_noise_limit = bln_x_height * 0.8; + + blob_it.set_to_list (word_res->outword->blob_list ()); + //normalised + blob_count = blob_it.length (); + ASSERT_HOST (blob_count <= 512); + if (blob_count < 5) + return -1; //too short to split + /* Get the noise scores for all blobs */ + + #ifndef SECURE_NAMES + if (debug_fix_space_level > 5) + tprintf ("FP fixspace Noise metrics for \"%s\": ", + word_res->best_choice->string ().string ()); + #endif + + for (i = 0; i < blob_count; i++, blob_it.forward ()) { + if (word_res->reject_map[i].accepted ()) + noise_score[i] = non_noise_limit; + else + noise_score[i] = blob_noise_score (blob_it.data ()); + + if (debug_fix_space_level > 5) + tprintf ("%1.1f ", noise_score[i]); + } + if (debug_fix_space_level > 5) + tprintf ("\n"); + + /* Now find the worst one which is far enough away from the end of the word */ + + non_noise_count = 0; + for (i = 0; + (i < blob_count) && (non_noise_count < fixsp_non_noise_limit); i++) { + if (noise_score[i] >= non_noise_limit) + non_noise_count++; + } + if (non_noise_count < fixsp_non_noise_limit) + return -1; + min_noise_blob = i; + + non_noise_count = 0; + for (i = blob_count - 1; + (i >= 0) && (non_noise_count < fixsp_non_noise_limit); i--) { + if (noise_score[i] >= non_noise_limit) + non_noise_count++; + } + if (non_noise_count < fixsp_non_noise_limit) + return -1; + max_noise_blob = i; + + if (min_noise_blob > max_noise_blob) + return -1; + + *worst_noise_score = small_limit; + worst_noise_blob = -1; + for (i = min_noise_blob; i <= max_noise_blob; i++) { + if (noise_score[i] < *worst_noise_score) { + worst_noise_blob = i; + *worst_noise_score = noise_score[i]; + } + } + return worst_noise_blob; +} + + +float blob_noise_score(PBLOB *blob) { + OUTLINE_IT outline_it; + BOX box; //BB of outline + INT16 outline_count = 0; + INT16 max_dimension; + INT16 largest_outline_dimension = 0; + + outline_it.set_to_list (blob->out_list ()); + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); outline_it.forward ()) { + outline_count++; + box = outline_it.data ()->bounding_box (); + if (box.height () > box.width ()) + max_dimension = box.height (); + else + max_dimension = box.width (); + + if (largest_outline_dimension < max_dimension) + largest_outline_dimension = max_dimension; + } + + if (fixsp_noise_score_fixing) { + if (outline_count > 5) + //penalise LOTS of blobs + largest_outline_dimension *= 2; + + box = blob->bounding_box (); + + if ((box.bottom () > bln_baseline_offset * 4) || + (box.top () < bln_baseline_offset / 2)) + //Lax blob is if high or low + largest_outline_dimension /= 2; + } + return largest_outline_dimension; +} + + +void fixspace_dbg(WERD_RES *word) { + BOX box = word->word->bounding_box (); + BOOL8 show_map_detail = FALSE; + INT16 i; + + box.print (); + #ifndef SECURE_NAMES + tprintf (" \"%s\" ", word->best_choice->string ().string ()); + tprintf ("Blob count: %d (word); %d/%d (outword)\n", + word->word->gblob_list ()->length (), + word->outword->gblob_list ()->length (), + word->outword->rej_blob_list ()->length ()); + word->reject_map.print (debug_fp); + tprintf ("\n"); + if (show_map_detail) { + tprintf ("\"%s\"\n", word->best_choice->string ().string ()); + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + tprintf ("**** \"%c\" ****\n", word->best_choice->string ()[i]); + word->reject_map[i].full_print (debug_fp); + } + } + + tprintf ("Tess Accepted: %s\n", word->tess_accepted ? "TRUE" : "FALSE"); + tprintf ("Done flag: %s\n\n", word->done ? "TRUE" : "FALSE"); + #endif +} + + +/************************************************************************* + * fp_eval_word_spacing() + * Evaluation function for fixed pitch word lists. + * + * Basically, count the number of "nice" characters - those which are in tess + * acceptable words or in dict words and are not rejected. + * Penalise any potential noise chars + *************************************************************************/ + +INT16 fp_eval_word_spacing(WERD_RES_LIST &word_res_list) { + WERD_RES_IT word_it(&word_res_list); + WERD_RES *word; + PBLOB_IT blob_it; + INT16 word_length; + INT16 score = 0; + INT16 i; + const char *chs; + float small_limit = bln_x_height * fixsp_small_outlines_size; + + if (!fixsp_fp_eval) + return (eval_word_spacing (word_res_list)); + + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + word_length = word->reject_map.length (); + chs = word->best_choice->string ().string (); + if ((word->done || + word->tess_accepted) || + (word->best_choice->permuter () == SYSTEM_DAWG_PERM) || + (word->best_choice->permuter () == FREQ_DAWG_PERM) || + (word->best_choice->permuter () == USER_DAWG_PERM) || + (safe_dict_word (chs) > 0)) { + blob_it.set_to_list (word->outword->blob_list ()); + for (i = 0; i < word_length; i++, blob_it.forward ()) { + if ((chs[i] == ' ') || + (blob_noise_score (blob_it.data ()) < small_limit)) + score -= 1; //penalise possibly erroneous non-space + + else if (word->reject_map[i].accepted ()) + score++; + } + } + } + if (score < 0) + score = 0; + return score; +} diff --git a/ccmain/fixspace.h b/ccmain/fixspace.h new file mode 100644 index 0000000000..75a2892c1b --- /dev/null +++ b/ccmain/fixspace.h @@ -0,0 +1,72 @@ +/****************************************************************** + * File: fixspace.h (Formerly fixspace.h) + * Description: Implements a pass over the page res, exploring the alternative + * spacing possibilities, trying to use context to improve the + word spacing +* Author: Phil Cheatle +* Created: Thu Oct 21 11:38:43 BST 1993 +* +* (C) Copyright 1993, Hewlett-Packard Ltd. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +* +**********************************************************************/ + +#ifndef FIXSPACE_H +#define FIXSPACE_H + +#include "pageres.h" +#include "varable.h" +#include "ocrclass.h" +#include "notdll.h" + +extern BOOL_VAR_H (fixsp_check_for_fp_noise_space, TRUE, +"Try turning noise to space in fixed pitch"); +extern BOOL_VAR_H (fixsp_fp_eval, TRUE, "Use alternate evaluation for fp"); +extern BOOL_VAR_H (fixsp_noise_score_fixing, TRUE, "More sophisticated?"); +extern INT_VAR_H (fixsp_non_noise_limit, 1, +"How many non-noise blbs either side?"); +extern double_VAR_H (fixsp_small_outlines_size, 0.28, +"Small if lt xht x this"); +extern BOOL_VAR_H (fixsp_ignore_punct, TRUE, "In uniform spacing calc"); +extern BOOL_VAR_H (fixsp_numeric_fix, TRUE, "Try to deal with numeric punct"); +extern BOOL_VAR_H (fixsp_prefer_joined_1s, TRUE, "Arbitrary boost"); +extern BOOL_VAR_H (tessedit_test_uniform_wd_spacing, FALSE, +"Limit context word spacing"); +extern BOOL_VAR_H (tessedit_prefer_joined_punct, FALSE, +"Reward punctation joins"); +extern INT_VAR_H (fixsp_done_mode, 1, "What constitues done for spacing"); +extern INT_VAR_H (debug_fix_space_level, 0, "Contextual fixspace debug"); +extern STRING_VAR_H (numeric_punctuation, ".,", +"Punct. chs expected WITHIN numbers"); +void fix_fuzzy_spaces( //find fuzzy words + volatile ETEXT_DESC *monitor, //progress monitor + INT32 word_count, //count of words in doc + PAGE_RES *page_res); +void fix_fuzzy_space_list( //space explorer + WERD_RES_LIST &best_perm, + ROW *row); +void initialise_search(WERD_RES_LIST &src_list, WERD_RES_LIST &new_list); +void match_current_words(WERD_RES_LIST &words, ROW *row); +INT16 eval_word_spacing(WERD_RES_LIST &word_res_list); +BOOL8 digit_or_numeric_punct(WERD_RES *word, char ch); +void transform_to_next_perm(WERD_RES_LIST &words); +void dump_words(WERD_RES_LIST &perm, INT16 score, INT16 mode, BOOL8 improved); +BOOL8 uniformly_spaced( //sensible word + WERD_RES *word); +BOOL8 fixspace_thinks_word_done(WERD_RES *word); +void fix_sp_fp_word(WERD_RES_IT &word_res_it, ROW *row); +void fix_noisy_space_list(WERD_RES_LIST &best_perm, ROW *row); +void break_noisiest_blob_word(WERD_RES_LIST &words); +INT16 worst_noise_blob(WERD_RES *word_res, float *worst_noise_score); +float blob_noise_score(PBLOB *blob); +void fixspace_dbg(WERD_RES *word); +INT16 fp_eval_word_spacing(WERD_RES_LIST &word_res_list); +#endif diff --git a/ccmain/fixxht.cpp b/ccmain/fixxht.cpp new file mode 100644 index 0000000000..f1339dabbf --- /dev/null +++ b/ccmain/fixxht.cpp @@ -0,0 +1,790 @@ +/********************************************************************** + * File: fixxht.cpp (Formerly fixxht.c) + * Description: Improve x_ht and look out for case inconsistencies + * Author: Phil Cheatle + * Created: Thu Aug 5 14:11:08 BST 1993 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include +#include "varable.h" +#include "tessvars.h" +#include "control.h" +#include "reject.h" +#include "fixxht.h" +#include "secname.h" + +#define EXTERN + +EXTERN double_VAR (x_ht_fraction_of_caps_ht, 0.7, +"Fract of cps ht est of xht"); +EXTERN double_VAR (x_ht_variation, 0.35, +"Err band as fract of caps/xht dist"); +EXTERN double_VAR (x_ht_sub_variation, 0.5, +"Err band as fract of caps/xht dist"); +EXTERN BOOL_VAR (rej_trial_ambigs, TRUE, +"reject x-ht ambigs when under trial"); +EXTERN BOOL_VAR (x_ht_conservative_ambigs, FALSE, +"Dont rely on ambigs + maxht"); +EXTERN BOOL_VAR (x_ht_check_est, TRUE, "Cross check estimates"); +EXTERN BOOL_VAR (x_ht_case_flip, FALSE, "Flip or reject suspect case"); +EXTERN BOOL_VAR (x_ht_include_dodgy_blobs, TRUE, +"Include blobs with possible noise?"); +EXTERN BOOL_VAR (x_ht_limit_flip_trials, TRUE, +"Dont do trial flips when ambigs are close to xht?"); +EXTERN BOOL_VAR (rej_use_check_block_occ, TRUE, +"Analyse rejection behaviour"); + +EXTERN STRING_VAR (chs_non_ambig_caps_ht, +"!#$%&()/12346789?ABDEFGHIKLNQRT[]\\bdfhkl", +"Reliable ascenders"); +EXTERN STRING_VAR (chs_x_ht, "acegmnopqrsuvwxyz", "X height chars"); +EXTERN STRING_VAR (chs_non_ambig_x_ht, "aenqr", "reliable X height chars"); +EXTERN STRING_VAR (chs_ambig_caps_x, "cCmMoO05sSuUvVwWxXzZ", +"X ht or caps ht chars"); +EXTERN STRING_VAR (chs_bl_ambig_caps_x, "pPyY", " Caps or descender ambigs"); + +/* The following arent used in this module but are used in applybox.c */ +EXTERN STRING_VAR (chs_caps_ht, +"!#$%&()/0123456789?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]\\bdfhkl{|}", +"Ascender chars"); +EXTERN STRING_VAR (chs_desc, "gjpqy", "Descender chars"); +EXTERN STRING_VAR (chs_non_ambig_bl, +"!#$%&01246789?ABCDEFGHIKLMNORSTUVWXYZabcdehiklmnorstuvwxz", +"Reliable baseline chars"); +EXTERN STRING_VAR (chs_odd_top, "ijt", "Chars with funny ascender region"); +EXTERN STRING_VAR (chs_odd_bot, "()35JQ[]\\/{}|", "Chars with funny base"); + +/* The following arent used but are defined for completeness */ +EXTERN STRING_VAR (chs_bl, +"!#$%&()/01246789?ABCDEFGHIJKLMNOPRSTUVWXYZ[]\\abcdefhiklmnorstuvwxz{}", +"Baseline chars"); +EXTERN STRING_VAR (chs_non_ambig_desc, "gq", "Reliable descender chars"); + +/************************************************************************* + * re_estimate_x_ht() + * + * Walk the blobs in the word together with the text string and reject map. + * NOTE: All evaluation is done on the baseline normalised word. This is so that + * the BOX class can be used (integer). The reasons for this are: + * a) We must use the outword - ie the Tess result + * b) The outword is always converted to integer representation as that is how + * Tess works + * c) We would like to use the BOX class, cos its there - this is integer + * precision. + * d) If we de-normed the outword we would get rounding errors and would find + * that integers are too imprecise (x-height around 15 pixels instead of a + * scale of 128 in bln form. + * CONVINCED? + * + * A) Try to re-estimatate x-ht and caps ht from confirmed pts in word. + * + * FOR each non reject blob + * IF char is baseline posn ambiguous + * Remove ambiguity by comparing its posn with respect to baseline. + * IF char is a confirmed x-ht char + * Add x-ht posn to confirmed_x_ht pts for word + * IF char is a confirmed caps-ht char + * Add blob_ht to caps ht pts for word + * + * IF Std Dev of caps hts < 2 (AND # samples > 0) + * Use mean as caps ht estimate (Dont use median as we can expect a + * fair variation between the heights of the NON_AMBIG_CAPS_HT_CHS) + * IF Std Dev of caps hts >= 2 (AND # samples > 0) + * Suspect small caps font. + * Look for 2 clusters, each with Std Dev < 2. + * IF 2 clusters found + * Pick the smaller median as the caps ht estimate of the smallcaps. + * + * IF failed to estimate a caps ht + * Use the median caps ht if there is one, + * ELSE use the caps ht estimate of the previous word. NO!!! + * + * + * IF there are confirmed x-height chars + * Estimate confirmed x-height as the median value + * ELSE IF there is a confirmed caps ht + * Estimate confirmed x-height as a fraction of confirmed caps ht value + * ELSE + * Use the value for the previous word or the row value if this is the + * first word in the block. NO!!! + * + * B) Add in case ambiguous blobs based on confirmed x-ht/caps ht, changing case + * as necessary. Reestimate caps ht and x-ht as in A, using the extended + * clusters. + * + * C) If word contains rejects, and x-ht estimate significantly differs from + * original estimate, return TRUE so that the word can be rematched + *************************************************************************/ + +void re_estimate_x_ht( //improve for 1 word + WERD_RES *word_res, //word to do + float *trial_x_ht //new match value + ) { + PBLOB_IT blob_it; + INT16 blob_ht_above_baseline; + + const char *word_str; + INT16 i; + + STATS all_blobs_ht (0, 300); //every blob in word + STATS x_ht (0, 300); //confirmed pts in wd + STATS caps_ht (0, 300); //confirmed pts in wd + STATS case_ambig (0, 300); //lower case ambigs + + INT16 rej_blobs_count = 0; + INT16 rej_blobs_max_height = 0; + INT32 rej_blobs_max_area = 0; + float x_ht_ok_variation; + float max_blob_ht; + float marginally_above_x_ht; + + BOX blob_box; //blob bounding box + float est_x_ht = 0.0; //word estimate + float est_caps_ht = 0.0; //word estimate + //based on hard data? + BOOL8 est_caps_ht_certain = FALSE; + BOOL8 est_x_ht_certain = FALSE;//based on hard data? + BOOL8 trial = FALSE; //Sepeculative values? + BOOL8 no_comment = FALSE; //No change in xht + float ambig_lc_x_est; + float ambig_uc_caps_est; + INT16 x_ht_ambigs = 0; + INT16 caps_ht_ambigs = 0; + + /* Calculate default variation of blob x_ht from bln x_ht for bln word */ + x_ht_ok_variation = + (bln_x_height / x_ht_fraction_of_caps_ht - bln_x_height) * x_ht_variation; + + word_str = word_res->best_choice->string ().string (); + /* + Cycle blobs, allocating to one of the stats sets when possible. + */ + blob_it.set_to_list (word_res->outword->blob_list ()); + for (blob_it.mark_cycle_pt (), i = 0; + !blob_it.cycled_list (); blob_it.forward (), i++) { + if (!dodgy_blob (blob_it.data ())) { + blob_box = blob_it.data ()->bounding_box (); + blob_ht_above_baseline = blob_box.top () - bln_baseline_offset; + all_blobs_ht.add (blob_ht_above_baseline, 1); + + if (word_res->reject_map[i].rejected ()) { + rej_blobs_count++; + if (blob_box.height () > rej_blobs_max_height) + rej_blobs_max_height = blob_box.height (); + if (blob_box.area () > rej_blobs_max_area) + rej_blobs_max_area = blob_box.area (); + } + else { + if (STRING (chs_non_ambig_x_ht).contains (word_str[i])) + x_ht.add (blob_ht_above_baseline, 1); + + if (STRING (chs_non_ambig_caps_ht).contains (word_str[i])) + caps_ht.add (blob_ht_above_baseline, 1); + + if (STRING (chs_ambig_caps_x).contains (word_str[i])) { + case_ambig.add (blob_ht_above_baseline, 1); + if (STRING (chs_x_ht).contains (word_str[i])) + x_ht_ambigs++; + else + caps_ht_ambigs++; + } + + if (STRING (chs_bl_ambig_caps_x).contains (word_str[i])) { + if (STRING (chs_x_ht).contains (word_str[i])) { + /* confirm x_height provided > 15% total height below baseline */ + if ((bln_baseline_offset - blob_box.bottom ()) / + (float) blob_box.height () > 0.15) + x_ht.add (blob_ht_above_baseline, 1); + } + else { + /* confirm caps_height provided < 5% total height below baseline */ + if ((bln_baseline_offset - blob_box.bottom ()) / + (float) blob_box.height () < 0.05) + caps_ht.add (blob_ht_above_baseline, 1); + } + } + } + } + } + est_caps_ht = estimate_from_stats (caps_ht); + est_x_ht = estimate_from_stats (x_ht); + est_ambigs(word_res, case_ambig, &ambig_lc_x_est, &ambig_uc_caps_est); + max_blob_ht = all_blobs_ht.ile (0.9999); + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) { + tprintf ("Mode20:A: %s ", word_str); + word_res->reject_map.print (debug_fp); + tprintf (" XHT:%f CAP:%f MAX:%f AMBIG X:%f CAP:%f\n", + est_x_ht, est_caps_ht, max_blob_ht, + ambig_lc_x_est, ambig_uc_caps_est); + } + #endif + if (!x_ht_conservative_ambigs && + (ambig_lc_x_est > 0) && + (ambig_lc_x_est == ambig_uc_caps_est) && + (max_blob_ht > ambig_lc_x_est + x_ht_ok_variation)) { + //may be zero but believe xht + ambig_uc_caps_est = est_caps_ht; + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:B: Fiddle ambig_uc_caps_est to %f\n", + ambig_lc_x_est); + #endif + } + + /* Now make some estimates */ + + if ((est_x_ht > 0) || + (est_caps_ht > 0) || + ((ambig_lc_x_est > 0) && (ambig_lc_x_est != ambig_uc_caps_est))) { + /* There is some sensible data to go on so make the most of it. */ + if (debug_x_ht_level >= 20) + tprintf ("Mode20:C: Sensible Data\n", ambig_lc_x_est); + if (est_x_ht > 0) { + est_x_ht_certain = TRUE; + if (est_caps_ht == 0) { + if ((ambig_uc_caps_est > ambig_lc_x_est) && + (ambig_uc_caps_est > est_x_ht + x_ht_ok_variation)) + est_caps_ht = ambig_uc_caps_est; + else + est_caps_ht = est_x_ht / x_ht_fraction_of_caps_ht; + } + if (case_ambig.get_total () > 0) + improve_estimate(word_res, est_x_ht, est_caps_ht, x_ht, caps_ht); + est_caps_ht_certain = caps_ht.get_total () > 0; + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:D: Est from xht XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + else if (est_caps_ht > 0) { + est_caps_ht_certain = TRUE; + if ((ambig_lc_x_est > 0) && + (ambig_lc_x_est < est_caps_ht - x_ht_ok_variation)) + est_x_ht = ambig_lc_x_est; + else + est_x_ht = est_caps_ht * x_ht_fraction_of_caps_ht; + if (ambig_lc_x_est + ambig_uc_caps_est > 0) + improve_estimate(word_res, est_x_ht, est_caps_ht, x_ht, caps_ht); + est_x_ht_certain = x_ht.get_total () > 0; + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:E: Est from caps XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + else { + /* Do something based on case ambig chars alone - we have guessed that the + ambigs are lower case. */ + est_x_ht = ambig_lc_x_est; + est_x_ht_certain = TRUE; + if (ambig_uc_caps_est > ambig_lc_x_est) { + est_caps_ht = ambig_uc_caps_est; + est_caps_ht_certain = TRUE; + } + else + est_caps_ht = est_x_ht / x_ht_fraction_of_caps_ht; + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:F: Est from ambigs XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + /* Check for sane interpretation of evidence: + Try shifting caps ht if min certain caps ht is not significantly greater + than the estimated x ht or the max certain x ht is not significantly less + than the estimated caps ht. */ + if (x_ht_check_est) { + if ((caps_ht.get_total () > 0) && + (est_x_ht + x_ht_ok_variation >= caps_ht.ile (0.0001))) { + trial = TRUE; + est_caps_ht = est_x_ht; + est_x_ht = x_ht_fraction_of_caps_ht * est_caps_ht; + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:G: Trial XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + else if ((x_ht.get_total () > 0) && + (est_caps_ht - x_ht_ok_variation <= x_ht.ile (0.9999))) { + trial = TRUE; + est_x_ht = est_caps_ht; + est_caps_ht = est_x_ht / x_ht_fraction_of_caps_ht; + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:H: Trial XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + } + } + + else { + /* There is no sensible data so we're in the dark. */ + + marginally_above_x_ht = bln_x_height + + x_ht_ok_variation * x_ht_sub_variation; + /* + If there are no rejects, or the only rejects have a narrow height, or have + a small area compared to a normal char, then estimate the x-height as the + original one. (I.e dont fiddle about if the only rejects look like + punctuation) - we use max height as mean or median will be too low if + there are only two blobs - Eg "F." + */ + + if (debug_x_ht_level >= 20) + tprintf ("Mode20:I: In the dark\n"); + + if ((rej_blobs_count == 0) || + (rej_blobs_max_height < 0.3 * max_blob_ht) || + (rej_blobs_max_area < 0.3 * max_blob_ht * max_blob_ht)) { + no_comment = TRUE; + if (debug_x_ht_level >= 20) + tprintf ("Mode20:J: No comment due to no rejects\n"); + } + else if (x_ht_limit_flip_trials && + ((max_blob_ht < marginally_above_x_ht) || + ((ambig_lc_x_est > 0) && + (ambig_lc_x_est == ambig_uc_caps_est) && + (ambig_lc_x_est < marginally_above_x_ht)))) { + no_comment = TRUE; + if (debug_x_ht_level >= 20) + tprintf ("Mode20:K: No comment as close to xht %f < %f\n", + ambig_lc_x_est, marginally_above_x_ht); + } + else if (x_ht_conservative_ambigs && (ambig_uc_caps_est > 0)) { + trial = TRUE; + est_caps_ht = ambig_lc_x_est; + est_x_ht = x_ht_fraction_of_caps_ht * est_caps_ht; + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:L: Trial XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + /* + If the top of the word is nowhere near where we expect ascenders to be + (less than half the x_ht -> caps_ht distance) - suspect an all caps word + at the x-ht. Estimate x-ht accordingly - but only as a TRIAL! + NOTE we do NOT check location of baseline. Commas can descend as much as + real descenders so we would need to do something to make sure that any + disqualifying descenders were not at the end. + */ + else { + if (max_blob_ht < + (bln_x_height + bln_x_height / x_ht_fraction_of_caps_ht) / 2.0) { + trial = TRUE; + est_x_ht = x_ht_fraction_of_caps_ht * max_blob_ht; + est_caps_ht = max_blob_ht; + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:M: Trial XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + else { + no_comment = TRUE; + if (debug_x_ht_level >= 20) + tprintf ("Mode20:N: No comment as nothing else matched\n"); + } + } + } + + /* Sanity check - reject word if fails */ + + if (!no_comment && + ((est_x_ht > 2 * bln_x_height) || + (est_x_ht / word_res->denorm.scale () <= min_sane_x_ht_pixels) || + (est_caps_ht <= est_x_ht) || (est_caps_ht >= 2.5 * est_x_ht))) { + no_comment = TRUE; + if (!trial && rej_use_xht) { + if (debug_x_ht_level >= 2) { + tprintf ("Sanity check rejecting %s ", word_str); + word_res->reject_map.print (debug_fp); + tprintf ("\n"); + } + word_res->reject_map.rej_word_xht_fixup (); + + } + if (debug_x_ht_level >= 20) + tprintf ("Mode20:O: No comment as nothing else matched\n"); + } + + if (no_comment || trial) { + word_res->x_height = bln_x_height / word_res->denorm.scale (); + word_res->guessed_x_ht = TRUE; + word_res->caps_height = (bln_x_height / x_ht_fraction_of_caps_ht) / + word_res->denorm.scale (); + word_res->guessed_caps_ht = TRUE; + /* + Reject ambigs in the current word if we are uncertain and: + there are rejects OR + there is only one char which is an ambig OR + there is conflict between the case of the ambigs even though there is + no height separation Eg "Ms" recognised from "MS" + */ + if (rej_trial_ambigs && + ((word_res->reject_map.reject_count () > 0) || + (word_res->reject_map.length () == 1) || + ((x_ht_ambigs > 0) && (caps_ht_ambigs > 0)))) { + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 2) { + tprintf ("TRIAL Rej Ambigs %s ", word_str); + word_res->reject_map.print (debug_fp); + } + #endif + reject_ambigs(word_res); + if (debug_x_ht_level >= 2) { + tprintf (" "); + word_res->reject_map.print (debug_fp); + tprintf ("\n"); + } + } + } + else { + word_res->x_height = est_x_ht / word_res->denorm.scale (); + word_res->guessed_x_ht = !est_x_ht_certain; + word_res->caps_height = est_caps_ht / word_res->denorm.scale (); + word_res->guessed_caps_ht = !est_caps_ht_certain; + } + + if (!no_comment && (fabs (est_x_ht - bln_x_height) > x_ht_ok_variation)) + *trial_x_ht = est_x_ht / word_res->denorm.scale (); + else + *trial_x_ht = 0.0; + + #ifndef SECURE_NAMES + if (((*trial_x_ht > 0) && (debug_x_ht_level >= 3)) || + (debug_x_ht_level >= 5)) { + tprintf ("%s ", word_str); + word_res->reject_map.print (debug_fp); + tprintf + (" X:%0.2f Cps:%0.2f Mxht:%0.2f RJ MxHt:%d MxAr:%d Rematch:%c\n", + est_x_ht, est_caps_ht, max_blob_ht, rej_blobs_max_height, + rej_blobs_max_area, *trial_x_ht > 0 ? '*' : ' '); + } + #endif + +} + + +/************************************************************************* + * check_block_occ() + * Checks word for coarse block occupancy, rejecting more chars and flipping + * case of case ambiguous chars as required. + *************************************************************************/ + +void check_block_occ(WERD_RES *word_res) { + PBLOB_IT blob_it; + STRING new_string; + REJMAP new_map = word_res->reject_map; + WERD_CHOICE *new_choice; + + const char *word_str = word_res->best_choice->string ().string (); + INT16 i; + INT16 reject_count = 0; + char confirmed_char; + float x_ht; + float caps_ht; + + if (word_res->x_height > 0) + x_ht = word_res->x_height * word_res->denorm.scale (); + else + x_ht = bln_x_height; + + if (word_res->caps_height > 0) + caps_ht = word_res->caps_height * word_res->denorm.scale (); + else + caps_ht = x_ht / x_ht_fraction_of_caps_ht; + + blob_it.set_to_list (word_res->outword->blob_list ()); + + for (blob_it.mark_cycle_pt (), i = 0; + !blob_it.cycled_list (); blob_it.forward (), i++) { + new_string += word_str[i]; //default copy + if (word_res->reject_map[i].accepted ()) { + confirmed_char = check_blob_occ (word_str[i], + blob_it.data ()->bounding_box (). + top () - bln_baseline_offset, x_ht, + caps_ht); + + if (confirmed_char == '\0') { + if (rej_use_check_block_occ) { + new_map[i].setrej_xht_fixup (); + reject_count++; + } + } + else + new_string[i] = confirmed_char; + } + } + if ((reject_count > 0) || (new_string != word_str)) { + if (debug_x_ht_level >= 2) { + tprintf ("Shape Verification: %s ", word_str); + word_res->reject_map.print (debug_fp); + tprintf (" -> %s ", new_string.string ()); + new_map.print (debug_fp); + tprintf ("\n"); + } + new_choice = new WERD_CHOICE (new_string.string (), + word_res->best_choice->rating (), + word_res->best_choice->certainty (), + word_res->best_choice->permuter ()); + delete word_res->best_choice; + word_res->best_choice = new_choice; + word_res->reject_map = new_map; + } +} + + +/************************************************************************* + * check_blob_occ() + * + * Checks blob for position relative to position above baseline + * Returns 0 for reject, or (possibly case shifted) confirmed char + *************************************************************************/ + +char check_blob_occ(char proposed_char, + INT16 blob_ht_above_baseline, + float x_ht, + float caps_ht) { + BOOL8 blob_definite_x_ht; + BOOL8 blob_definite_caps_ht; + float acceptable_variation; + + acceptable_variation = (caps_ht - x_ht) * x_ht_variation; + /* ??? REJECT if expected descender and nothing significantly below BL */ + + /* ??? REJECT if expected ascender and nothing significantly above x-ht */ + + /* + IF AMBIG_CAPS_X_CHS + IF blob is definitely an ascender ( > xht + xht err )AND + char is an x-ht char + THEN + flip case + IF blob is defintiely an x-ht ( <= xht + xht err ) AND + char is an ascender char + THEN + flip case + */ + blob_definite_x_ht = blob_ht_above_baseline <= x_ht + acceptable_variation; + blob_definite_caps_ht = blob_ht_above_baseline >= + caps_ht - acceptable_variation; + + if (STRING (chs_ambig_caps_x).contains (proposed_char)) { + if ((!blob_definite_x_ht && !blob_definite_caps_ht) || + (proposed_char == '0' && !blob_definite_caps_ht) || + (proposed_char == 'o' && !blob_definite_x_ht)) + return '\0'; + + else if (blob_definite_caps_ht && + STRING (chs_x_ht).contains (proposed_char)) { + if (x_ht_case_flip) + //flip to upper case + return (char) toupper (proposed_char); + else + return '\0'; + } + + else if (blob_definite_x_ht && + !STRING (chs_x_ht).contains (proposed_char)) { + if (x_ht_case_flip) + //flip to lower case + return (char) tolower (proposed_char); + else + return '\0'; + } + } + else + if ((STRING (chs_non_ambig_x_ht).contains (proposed_char) + && !blob_definite_x_ht) + || (STRING (chs_non_ambig_caps_ht).contains (proposed_char) + && !blob_definite_caps_ht)) + return '\0'; + return proposed_char; +} + + +float estimate_from_stats(STATS &stats) { + if (stats.get_total () <= 0) + return 0.0; + else if (stats.get_total () >= 3) + return stats.ile (0.5); //median + else + return stats.mean (); +} + + +void improve_estimate(WERD_RES *word_res, + float &est_x_ht, + float &est_caps_ht, + STATS &x_ht, + STATS &caps_ht) { + PBLOB_IT blob_it; + INT16 blob_ht_above_baseline; + + const char *word_str; + INT16 i; + BOX blob_box; //blob bounding box + char confirmed_char; + float new_val; + + /* IMPROVE estimates here - if good estimates, and case ambig chars, + rescan blobs to fix case ambig blobs, re-estimate hts ??? maybe always do + it after deciding x-height + */ + + blob_it.set_to_list (word_res->outword->blob_list ()); + word_str = word_res->best_choice->string ().string (); + for (blob_it.mark_cycle_pt (), i = 0; + !blob_it.cycled_list (); blob_it.forward (), i++) { + if ((STRING (chs_ambig_caps_x).contains (word_str[i])) && + (!dodgy_blob (blob_it.data ()))) { + blob_box = blob_it.data ()->bounding_box (); + blob_ht_above_baseline = blob_box.top () - bln_baseline_offset; + confirmed_char = check_blob_occ (word_str[i], + blob_ht_above_baseline, + est_x_ht, est_caps_ht); + if (confirmed_char != '\0') + if (STRING (chs_x_ht).contains (confirmed_char)) + x_ht.add (blob_ht_above_baseline, 1); + else + caps_ht.add (blob_ht_above_baseline, 1); + } + } + new_val = estimate_from_stats (x_ht); + if (new_val > 0) + est_x_ht = new_val; + new_val = estimate_from_stats (caps_ht); + if (new_val > 0) + est_caps_ht = new_val; +} + + +void reject_ambigs( //rej any accepted xht ambig chars + WERD_RES *word) { + const char *word_str; + int i = 0; + + word_str = word->best_choice->string ().string (); + while (*word_str != '\0') { + if (STRING (chs_ambig_caps_x).contains (*word_str)) + word->reject_map[i].setrej_xht_fixup (); + word_str++; + i++; + } +} + + +void est_ambigs( //xht ambig ht stats + WERD_RES *word_res, + STATS &stats, + float *ambig_lc_x_est, //xht est + float *ambig_uc_caps_est //caps est + ) { + float x_ht_ok_variation; + STATS short_ambigs (0, 300); + STATS tall_ambigs (0, 300); + PBLOB_IT blob_it; + BOX blob_box; //blob bounding box + INT16 blob_ht_above_baseline; + + const char *word_str; + INT16 i; + float min; //min ambig ch ht + float max; //max ambig ch ht + float short_limit; // for lower case + float tall_limit; // for upper case + + x_ht_ok_variation = + (bln_x_height / x_ht_fraction_of_caps_ht - bln_x_height) * x_ht_variation; + + if (stats.get_total () == 0) { + *ambig_lc_x_est = 0; + *ambig_uc_caps_est = 0; + } + else { + min = stats.ile (0.0); + max = stats.ile (0.99999); + if ((max - min) < x_ht_ok_variation) { + *ambig_lc_x_est = *ambig_uc_caps_est = stats.mean (); + //close enough + } + else { + /* Try reclustering into lower and upper case chars */ + short_limit = min + (max - min) * x_ht_variation; + tall_limit = max - (max - min) * x_ht_variation; + word_str = word_res->best_choice->string ().string (); + blob_it.set_to_list (word_res->outword->blob_list ()); + for (blob_it.mark_cycle_pt (), i = 0; + !blob_it.cycled_list (); blob_it.forward (), i++) { + if (word_res->reject_map[i].accepted () && + STRING (chs_ambig_caps_x).contains (word_str[i]) && + (!dodgy_blob (blob_it.data ()))) { + blob_box = blob_it.data ()->bounding_box (); + blob_ht_above_baseline = + blob_box.top () - bln_baseline_offset; + if (blob_ht_above_baseline <= short_limit) + short_ambigs.add (blob_ht_above_baseline, 1); + else if (blob_ht_above_baseline >= tall_limit) + tall_ambigs.add (blob_ht_above_baseline, 1); + } + } + *ambig_lc_x_est = short_ambigs.mean (); + *ambig_uc_caps_est = tall_ambigs.mean (); + /* Cop out if we havent got sensible clusters. */ + if (*ambig_uc_caps_est - *ambig_lc_x_est <= x_ht_ok_variation) + *ambig_lc_x_est = *ambig_uc_caps_est = stats.mean (); + //close enough + } + } +} + + +/************************************************************************* + * dodgy_blob() + * Returns true if the blob has more than one outline, one above the other. + * These are dodgy as the top blob could be noise, causing the bounding box xht + * to be misleading + *************************************************************************/ + +BOOL8 dodgy_blob(PBLOB *blob) { + OUTLINE_IT outline_it = blob->out_list (); + INT16 highest_bottom = -MAX_INT16; + INT16 lowest_top = MAX_INT16; + BOX outline_box; + + if (x_ht_include_dodgy_blobs) + return FALSE; //no blob is ever dodgy + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); outline_it.forward ()) { + outline_box = outline_it.data ()->bounding_box (); + if (lowest_top > outline_box.top ()) + lowest_top = outline_box.top (); + if (highest_bottom < outline_box.bottom ()) + highest_bottom = outline_box.bottom (); + } + return highest_bottom >= lowest_top; +} diff --git a/ccmain/fixxht.h b/ccmain/fixxht.h new file mode 100644 index 0000000000..95ac5ec5d4 --- /dev/null +++ b/ccmain/fixxht.h @@ -0,0 +1,92 @@ +/********************************************************************** + * File: fixxht.h (Formerly fixxht.h) + * Description: Improve x_ht and look out for case inconsistencies + * Author: Phil Cheatle + * Created: Thu Aug 5 14:11:08 BST 1993 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef FIXXHT_H +#define FIXXHT_H + +#include "varable.h" +#include "statistc.h" +#include "pageres.h" +#include "notdll.h" + +extern double_VAR_H (x_ht_fraction_of_caps_ht, 0.7, +"Fract of cps ht est of xht"); +extern double_VAR_H (x_ht_variation, 0.35, +"Err band as fract of caps/xht dist"); +extern double_VAR_H (x_ht_sub_variation, 0.5, +"Err band as fract of caps/xht dist"); +extern BOOL_VAR_H (rej_trial_ambigs, TRUE, +"reject x-ht ambigs when under trial"); +extern BOOL_VAR_H (x_ht_conservative_ambigs, FALSE, +"Dont rely on ambigs + maxht"); +extern BOOL_VAR_H (x_ht_check_est, TRUE, "Cross check estimates"); +extern BOOL_VAR_H (x_ht_case_flip, FALSE, "Flip or reject suspect case"); +extern BOOL_VAR_H (x_ht_include_dodgy_blobs, TRUE, +"Include blobs with possible noise?"); +extern BOOL_VAR_H (x_ht_limit_flip_trials, TRUE, +"Dont do trial flips when ambigs are close to xht?"); +extern BOOL_VAR_H (rej_use_check_block_occ, TRUE, +"Analyse rejection behaviour"); +extern STRING_VAR_H (chs_non_ambig_caps_ht, +"!#$%&()/12346789?ABDEFGHIKLNQRT[]\\bdfhkl", +"Reliable ascenders"); +extern STRING_VAR_H (chs_x_ht, "acegmnopqrsuvwxyz", "X height chars"); +extern STRING_VAR_H (chs_non_ambig_x_ht, "aenqr", "reliable X height chars"); +extern STRING_VAR_H (chs_ambig_caps_x, "cCmMoO05sSuUvVwWxXzZ", +"X ht or caps ht chars"); +extern STRING_VAR_H (chs_bl_ambig_caps_x, "pPyY", +" Caps or descender ambigs"); +extern STRING_VAR_H (chs_caps_ht, +"!#$%&()/0123456789?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]\\bdfhkl{|}", +"Ascender chars"); +extern STRING_VAR_H (chs_desc, "gjpqy", "Descender chars"); +extern STRING_VAR_H (chs_non_ambig_bl, +"!#$%&01246789?ABCDEFGHIKLMNORSTUVWXYZabcdehiklmnorstuvwxz", +"Reliable baseline chars"); +extern STRING_VAR_H (chs_odd_top, "ijt", "Chars with funny ascender region"); +extern STRING_VAR_H (chs_odd_bot, "()35JQ[]\\/{}|", "Chars with funny base"); +extern STRING_VAR_H (chs_bl, +"!#$%&()/01246789?ABCDEFGHIJKLMNOPRSTUVWXYZ[]\\abcdefhiklmnorstuvwxz{}", +"Baseline chars"); +extern STRING_VAR_H (chs_non_ambig_desc, "gq", "Reliable descender chars"); +void re_estimate_x_ht( //improve for 1 word + WERD_RES *word_res, //word to do + float *trial_x_ht //new match value + ); +void check_block_occ(WERD_RES *word_res); +char check_blob_occ(char proposed_char, + INT16 blob_ht_above_baseline, + float x_ht, + float caps_ht); +float estimate_from_stats(STATS &stats); +void improve_estimate(WERD_RES *word_res, + float &est_x_ht, + float &est_caps_ht, + STATS &x_ht, + STATS &caps_ht); +void reject_ambigs( //rej any accepted xht ambig chars + WERD_RES *word); + //xht ambig ht stats +void est_ambigs(WERD_RES *word_res, + STATS &stats, + float *ambig_lc_x_est, //xht est + float *ambig_uc_caps_est //caps est + ); +BOOL8 dodgy_blob(PBLOB *blob); +#endif diff --git a/ccmain/imgscale.cpp b/ccmain/imgscale.cpp new file mode 100644 index 0000000000..d2d256c583 --- /dev/null +++ b/ccmain/imgscale.cpp @@ -0,0 +1,154 @@ +/********************************************************************** + * File: imgscale.cpp (Formerly dyn_prog.c) + * Description: Dynamic programming for smart scaling of images. + * Author: Phil Cheatle + * Created: Wed Nov 18 16:12:03 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +/************************************************************************* + * This is really Sheelagh's code that I've hacked into a more usable form. + * It is used by scaleim.c All I did to it was to change "factor" from int to + * float. + *************************************************************************/ +/************************************************************************ + * This version uses the result of the previous row to influence the + * current row's calculation. + ************************************************************************/ + +#include "mfcpch.h" +#include +#include +#include "errcode.h" + +#define f(xc, yc) ((xc - factor*yc)*(xc - factor*yc)) + +#define g(oldyc, yc, oldxc, xc) (factor*factor*(oldyc - yc)*(oldyc - yc)/(abs(oldxc - xc) + 1)) + +void +dyn_exit (const char s[]) { + fprintf (stderr, "%s", s); + err_exit(); +} + + +void dyn_prog( //The clever bit + int n, + int *x, + int *y, + int ymax, + int *oldx, + int *oldy, + int oldn, + float factor) { + int i, z, j, matchflag; + int **ymin; + float **F, fz; + + /* F[i][z] gives minimum over y <= z */ + + F = (float **) calloc (n, sizeof (float *)); + ymin = (int **) calloc (n, sizeof (int *)); + if ((F == NULL) || (ymin == NULL)) + dyn_exit ("Error in calloc\n"); + + for (i = 0; i < n; i++) { + F[i] = (float *) calloc (ymax - n + i + 1, sizeof (float)); + ymin[i] = (int *) calloc (ymax - n + i + 1, sizeof (int)); + if ((F[i] == NULL) || (ymin[i] == NULL)) + dyn_exit ("Error in calloc\n"); + } + + F[0][0] = f (x[0], 0); + /* find nearest transition of same sign (white to black) */ + j = 0; + while ((j < oldn) && (oldx[j] < x[0])) + j += 2; + if (j >= oldn) + j -= 2; + else if ((j - 2 >= 0) && ((x[0] - oldx[j - 2]) < (oldx[j] - x[0]))) + j -= 2; + if (abs (oldx[j] - x[0]) < factor) { + matchflag = 1; + F[0][0] += g (oldy[j], 0, oldx[j], x[0]); + } + else + matchflag = 0; + ymin[0][0] = 0; + + for (z = 1; z < ymax - n + 1; z++) { + fz = f (x[0], z); + /* add penalty for deviating from previous row if necessary */ + + if (matchflag) + fz += g (oldy[j], z, oldx[j], x[0]); + if (fz < F[0][z - 1]) { + F[0][z] = fz; + ymin[0][z] = z; + } + else { + F[0][z] = F[0][z - 1]; + ymin[0][z] = ymin[0][z - 1]; + } + } + + for (i = 1; i < n; i++) { + F[i][i] = f (x[i], i) + F[i - 1][i - 1]; + /* add penalty for deviating from previous row if necessary */ + if (j > 0) + j--; + else + j++; + while ((j < oldn) && (oldx[j] < x[i])) + j += 2; + if (j >= oldn) + j -= 2; + else if ((j - 2 >= 0) && ((x[i] - oldx[j - 2]) < (oldx[j] - x[i]))) + j -= 2; + if (abs (oldx[j] - x[i]) < factor) { + matchflag = 1; + F[i][i] += g (oldy[j], i, oldx[j], x[i]); + } + else + matchflag = 0; + ymin[i][i] = i; + for (z = i + 1; z < ymax - n + i + 1; z++) { + fz = f (x[i], z) + F[i - 1][z - 1]; + /* add penalty for deviating from previous row if necessary */ + if (matchflag) + fz += g (oldy[j], z, oldx[j], x[i]); + if (fz < F[i][z - 1]) { + F[i][z] = fz; + ymin[i][z] = z; + } + else { + F[i][z] = F[i][z - 1]; + ymin[i][z] = ymin[i][z - 1]; + } + } + } + + y[n - 1] = ymin[n - 1][ymax - 1]; + for (i = n - 2; i >= 0; i--) + y[i] = ymin[i][y[i + 1] - 1]; + + for (i = 0; i < n; i++) { + free (F[i]); + free (ymin[i]); + } + free(F); + free(ymin); + + return; +} diff --git a/ccmain/imgscale.h b/ccmain/imgscale.h new file mode 100644 index 0000000000..335c0b0148 --- /dev/null +++ b/ccmain/imgscale.h @@ -0,0 +1,32 @@ +/********************************************************************** + * File: imgscale.h (Formerly dyn_prog.h) + * Description: Dynamic programming for smart scaling of images. + * Author: Phil Cheatle + * Created: Wed Nov 18 16:12:03 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef IMGSCALE_H +#define IMGSCALE_H + +void dyn_prog( //The clever bit + int n, + int *x, + int *y, + int ymax, + int *oldx, + int *oldy, + int oldn, + float factor); +#endif diff --git a/ccmain/matmatch.cpp b/ccmain/matmatch.cpp new file mode 100644 index 0000000000..1b13b9285a --- /dev/null +++ b/ccmain/matmatch.cpp @@ -0,0 +1,404 @@ +/********************************************************************** + * File: matmatch.cpp (Formerly matrix_match.c) + * Description: matrix matching routines for Tessedit + * Author: Chris Newton + * Created: Wed Nov 24 15:57:41 GMT 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include +#include +#include +#ifdef __UNIX__ +#include +#endif +#include "tessvars.h" +#include "stderr.h" +#include "img.h" +#include "evnts.h" +#include "showim.h" +#include "hosthplb.h" +#include "grphics.h" +#include "evnts.h" +#include "adaptions.h" +#include "matmatch.h" +#include "secname.h" + +#define EXTERN + +EXTERN BOOL_VAR (tessedit_display_mm, FALSE, "Display matrix matches"); +EXTERN BOOL_VAR (tessedit_mm_debug, FALSE, +"Print debug information for matrix matcher"); +EXTERN INT_VAR (tessedit_mm_prototype_min_size, 3, +"Smallest number of samples in a cluster for a prototype to be used"); + +// Colours for displaying the match +#define BB_COLOUR 0 +#define BW_COLOUR 1 +#define WB_COLOUR 3 +#define UB_COLOUR 5 +#define BU_COLOUR 7 +#define UU_COLOUR 9 +#define WU_COLOUR 11 +#define UW_COLOUR 13 +#define WW_COLOUR 15 + +#define BINIM_BLACK 0 +#define BINIM_WHITE 1 + +float matrix_match( // returns match score + IMAGE *image1, + IMAGE *image2) { + ASSERT_HOST (image1->get_bpp () == 1 && image2->get_bpp () == 1); + + if (image1->get_xsize () >= image2->get_xsize ()) + return match1 (image1, image2); + else + return match1 (image2, image1); +} + + +float match1( /* returns match score */ + IMAGE *image_w, + IMAGE *image_n) { + INT32 x_offset; + INT32 y_offset; + INT32 x_size = image_w->get_xsize (); + INT32 y_size; + INT32 x_size2 = image_n->get_xsize (); + INT32 y_size2; + IMAGE match_image; + IMAGELINE imline_w; + IMAGELINE imline_n; + IMAGELINE match_imline; + INT32 x; + INT32 y; + float sum = 0.0; + + x_offset = (image_w->get_xsize () - image_n->get_xsize ()) / 2; + + ASSERT_HOST (x_offset >= 0); + match_imline.init (x_size); + + sum = 0; + + if (image_w->get_ysize () < image_n->get_ysize ()) { + y_size = image_n->get_ysize (); + y_size2 = image_w->get_ysize (); + y_offset = (y_size - y_size2) / 2; + + if (tessedit_display_mm && !tessedit_mm_use_prototypes) + tprintf ("I1 (%d, %d), I2 (%d, %d), MI (%d, %d)\n", x_size, + image_w->get_ysize (), x_size2, image_n->get_ysize (), + x_size, y_size); + + match_image.create (x_size, y_size, 4); + + for (y = 0; y < y_offset; y++) { + image_n->fast_get_line (0, y, x_size2, &imline_n); + for (x = 0; x < x_size2; x++) { + if (imline_n.pixels[x] == BINIM_BLACK) { + sum += -1; + match_imline.pixels[x] = UB_COLOUR; + } + else { + match_imline.pixels[x] = UW_COLOUR; + } + } + match_image.fast_put_line (x_offset, y, x_size2, &match_imline); + } + + for (y = y_offset + y_size2; y < y_size; y++) { + image_n->fast_get_line (0, y, x_size2, &imline_n); + for (x = 0; x < x_size2; x++) { + if (imline_n.pixels[x] == BINIM_BLACK) { + sum += -1.0; + match_imline.pixels[x] = UB_COLOUR; + } + else { + match_imline.pixels[x] = UW_COLOUR; + } + } + match_image.fast_put_line (x_offset, y, x_size2, &match_imline); + } + + for (y = y_offset; y < y_offset + y_size2; y++) { + image_w->fast_get_line (0, y - y_offset, x_size, &imline_w); + image_n->fast_get_line (0, y, x_size2, &imline_n); + for (x = 0; x < x_offset; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1.0; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + + for (x = x_offset + x_size2; x < x_size; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1.0; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + + for (x = x_offset; x < x_offset + x_size2; x++) { + if (imline_n.pixels[x - x_offset] == imline_w.pixels[x]) { + sum += 1.0; + if (imline_w.pixels[x] == BINIM_BLACK) + match_imline.pixels[x] = BB_COLOUR; + else + match_imline.pixels[x] = WW_COLOUR; + } + else { + sum += -1.0; + if (imline_w.pixels[x] == BINIM_BLACK) + match_imline.pixels[x] = BW_COLOUR; + else + match_imline.pixels[x] = WB_COLOUR; + } + } + + match_image.fast_put_line (0, y, x_size, &match_imline); + } + } + else { + y_size = image_w->get_ysize (); + y_size2 = image_n->get_ysize (); + y_offset = (y_size - y_size2) / 2; + + if (tessedit_display_mm && !tessedit_mm_use_prototypes) + tprintf ("I1 (%d, %d), I2 (%d, %d), MI (%d, %d)\n", x_size, + image_w->get_ysize (), x_size2, image_n->get_ysize (), + x_size, y_size); + + match_image.create (x_size, y_size, 4); + + for (y = 0; y < y_offset; y++) { + image_w->fast_get_line (0, y, x_size, &imline_w); + for (x = 0; x < x_size; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + match_image.fast_put_line (0, y, x_size, &match_imline); + } + + for (y = y_offset + y_size2; y < y_size; y++) { + image_w->fast_get_line (0, y, x_size, &imline_w); + for (x = 0; x < x_size; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + match_image.fast_put_line (0, y, x_size, &match_imline); + } + + for (y = y_offset; y < y_offset + y_size2; y++) { + image_w->fast_get_line (0, y, x_size, &imline_w); + image_n->fast_get_line (0, y - y_offset, x_size2, &imline_n); + for (x = 0; x < x_offset; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1.0; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + + for (x = x_offset + x_size2; x < x_size; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1.0; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + + for (x = x_offset; x < x_offset + x_size2; x++) { + if (imline_n.pixels[x - x_offset] == imline_w.pixels[x]) { + sum += 1.0; + if (imline_w.pixels[x] == BINIM_BLACK) + match_imline.pixels[x] = BB_COLOUR; + else + match_imline.pixels[x] = WW_COLOUR; + } + else { + sum += -1.0; + if (imline_w.pixels[x] == BINIM_BLACK) + match_imline.pixels[x] = BW_COLOUR; + else + match_imline.pixels[x] = WB_COLOUR; + } + } + + match_image.fast_put_line (0, y, x_size, &match_imline); + } + } + +#ifndef GRAPHICS_DISABLED + if (tessedit_display_mm && !tessedit_mm_use_prototypes) { + tprintf ("Match score %f\n", 1.0 - sum / (x_size * y_size)); + display_images(image_w, image_n, &match_image); + } +#endif + + if (tessedit_mm_debug) + tprintf ("Match score %f\n", 1.0 - sum / (x_size * y_size)); + + return (1.0 - sum / (x_size * y_size)); +} + + +/************************************************************************* + * display_images() + * + * Show a pair of images, plus the match image + * + *************************************************************************/ + +#ifndef GRAPHICS_DISABLED +void display_images(IMAGE *image_w, IMAGE *image_n, IMAGE *match_image) { + WINDOW w_im_window; + WINDOW n_im_window; + WINDOW match_window; + GRAPHICS_EVENT event; //output event + INT16 i; + + // xmin xmax ymin ymax + w_im_window = create_window ("Image 1", SCROLLINGWIN, 20, 100, 10 * image_w->get_xsize (), 10 * image_w->get_ysize (), 0, image_w->get_xsize (), 0, image_w->get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(w_im_window); + show_sub_image (image_w, + 0, 0, + image_w->get_xsize (), image_w->get_ysize (), + w_im_window, 0, 0); + + line_color_index(w_im_window, RED); + for (i = 1; i < image_w->get_xsize (); i++) { + move2d (w_im_window, i, 0); + draw2d (w_im_window, i, image_w->get_ysize ()); + } + for (i = 1; i < image_w->get_ysize (); i++) { + move2d (w_im_window, 0, i); + draw2d (w_im_window, image_w->get_xsize (), i); + } + + // xmin xmax ymin ymax + n_im_window = create_window ("Image 2", SCROLLINGWIN, 240, 100, 10 * image_n->get_xsize (), 10 * image_n->get_ysize (), 0, image_n->get_xsize (), 0, image_n->get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(n_im_window); + show_sub_image (image_n, + 0, 0, + image_n->get_xsize (), image_n->get_ysize (), + n_im_window, 0, 0); + + line_color_index(n_im_window, RED); + for (i = 1; i < image_n->get_xsize (); i++) { + move2d (n_im_window, i, 0); + draw2d (n_im_window, i, image_n->get_ysize ()); + } + for (i = 1; i < image_n->get_ysize (); i++) { + move2d (n_im_window, 0, i); + draw2d (n_im_window, image_n->get_xsize (), i); + } + overlap_picture_ops(TRUE); + + // xmin xmax ymin ymax + match_window = create_window ("Match Result", SCROLLINGWIN, 460, 100, 10 * match_image->get_xsize (), 10 * match_image->get_ysize (), 0, match_image->get_xsize (), 0, match_image->get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(match_window); + show_sub_image (match_image, + 0, 0, + match_image->get_xsize (), match_image->get_ysize (), + match_window, 0, 0); + + line_color_index(match_window, RED); + for (i = 1; i < match_image->get_xsize (); i++) { + move2d (match_window, i, 0); + draw2d (match_window, i, match_image->get_ysize ()); + } + for (i = 1; i < match_image->get_ysize (); i++) { + move2d (match_window, 0, i); + draw2d (match_window, match_image->get_xsize (), i); + } + overlap_picture_ops(TRUE); + + await_event(match_window, TRUE, ANY_EVENT, &event); + destroy_window(w_im_window); + destroy_window(n_im_window); + destroy_window(match_window); +} + + +/************************************************************************* + * display_image() + * + * Show a single image + * + *************************************************************************/ + +WINDOW display_image(IMAGE *image, + const char *title, + INT32 x, + INT32 y, + BOOL8 wait) { + WINDOW im_window; + INT16 i; + GRAPHICS_EVENT event; //output event + + // xmin xmax ymin ymax + im_window = create_window (title, SCROLLINGWIN, x, y, 10 * image->get_xsize (), 10 * image->get_ysize (), 0, image->get_xsize (), 0, image->get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(im_window); + show_sub_image (image, + 0, 0, + image->get_xsize (), image->get_ysize (), im_window, 0, 0); + + line_color_index(im_window, RED); + for (i = 1; i < image->get_xsize (); i++) { + move2d (im_window, i, 0); + draw2d (im_window, i, image->get_ysize ()); + } + for (i = 1; i < image->get_ysize (); i++) { + move2d (im_window, 0, i); + draw2d (im_window, image->get_xsize (), i); + } + overlap_picture_ops(TRUE); + + if (wait) + await_event(im_window, TRUE, ANY_EVENT, &event); + + return im_window; +} +#endif diff --git a/ccmain/matmatch.h b/ccmain/matmatch.h new file mode 100644 index 0000000000..142c516060 --- /dev/null +++ b/ccmain/matmatch.h @@ -0,0 +1,48 @@ +/********************************************************************** + * File: matmatch.h (Formerly matrix_match.h) + * Description: matrix matching routines for Tessedit + * Author: Chris Newton + * Created: Wed Nov 24 15:57:41 GMT 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef MATMATCH_H +#define MATMATCH_H + +#include "img.h" +#include "hosthplb.h" +#include "notdll.h" + +#define BINIM_BLACK 0 +#define BINIM_WHITE 1 +#define BAD_MATCH 9999.0 + +extern BOOL_VAR_H (tessedit_display_mm, FALSE, "Display matrix matches"); +extern BOOL_VAR_H (tessedit_mm_debug, FALSE, +"Print debug information for matrix matcher"); +extern INT_VAR_H (tessedit_mm_prototype_min_size, 3, +"Smallest number of samples in a cluster for a prototype to be used"); +float matrix_match( // returns match score + IMAGE *image1, + IMAGE *image2); +float match1( /* returns match score */ + IMAGE *image_w, + IMAGE *image_n); +void display_images(IMAGE *image_w, IMAGE *image_n, IMAGE *match_image); +WINDOW display_image(IMAGE *image, + const char *title, + INT32 x, + INT32 y, + BOOL8 wait); +#endif diff --git a/ccmain/output.cpp b/ccmain/output.cpp new file mode 100644 index 0000000000..b779d98e2d --- /dev/null +++ b/ccmain/output.cpp @@ -0,0 +1,1185 @@ +/****************************************************************** + * File: output.cpp (Formerly output.c) + * Description: Output pass + * Author: Phil Cheatle + * Created: Thu Aug 4 10:56:08 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "ocrshell.h" +#include +#include +#ifdef __UNIX__ +#include +#include +#include +#endif +#include "mainblk.h" +#include "tfacep.h" +#include "tessvars.h" +#include "control.h" +#include "secname.h" +#include "reject.h" +#include "docqual.h" +#include "output.h" +#include "bestfirst.h" + +#define EXTERN + +#define EPAPER_EXT ".ep" +#define PAGE_YSIZE 3508 +#define CTRL_INSET '\024' //dc4=text inset +#define CTRL_FONT '\016' //so=font change +#define CTRL_DEFAULT '\017' //si=default font +#define CTRL_SHIFT '\022' //dc2=x shift +#define CTRL_TAB '\011' //tab +#define CTRL_NEWLINE '\012' //newline +#define CTRL_HARDLINE '\015' //cr +int NO_BLOCK = 0; //don't output block information +INT16 XOFFSET = 0; //the image can be a part of bigger picture and we want to have the original coordinates +INT16 YOFFSET = 0; + +EXTERN BOOL_EVAR (tessedit_write_block_separators, FALSE, +"Write block separators in output"); +EXTERN BOOL_VAR (tessedit_write_raw_output, FALSE, +"Write raw stuff to name.raw"); +EXTERN BOOL_EVAR (tessedit_write_output, TRUE, "Write text to name.txt"); +EXTERN BOOL_EVAR (tessedit_write_ratings, FALSE, +"Return ratings in IPEOCRAPI data"); +EXTERN BOOL_EVAR (tessedit_write_txt_map, TRUE, +"Write .txt to .etx map file"); +EXTERN BOOL_EVAR (tessedit_write_rep_codes, TRUE, +"Write repetition char code"); +EXTERN BOOL_EVAR (tessedit_write_unlv, FALSE, "Write .unlv output file"); +EXTERN STRING_EVAR (unrecognised_char, "|", +"Output char for unidentified blobs"); +EXTERN INT_EVAR (suspect_level, 99, "Suspect marker level"); +EXTERN INT_VAR (suspect_space_level, 100, +"Min suspect level for rejecting spaces"); +EXTERN INT_VAR (suspect_short_words, 2, +"Dont Suspect dict wds longer than this"); +EXTERN BOOL_VAR (suspect_constrain_1Il, FALSE, +"UNLV keep 1Il chars rejected"); +EXTERN double_VAR (suspect_rating_per_ch, 999.9, +"Dont touch bad rating limit"); +EXTERN double_VAR (suspect_accept_rating, -999.9, "Accept good rating limit"); + +EXTERN BOOL_EVAR (tessedit_minimal_rejection, FALSE, +"Only reject tess failures"); +EXTERN BOOL_VAR (tessedit_zero_rejection, FALSE, "Dont reject ANYTHING"); +EXTERN BOOL_VAR (tessedit_word_for_word, FALSE, +"Make output have exactly one word per WERD"); +EXTERN BOOL_VAR (tessedit_zero_kelvin_rejection, FALSE, +"Dont reject ANYTHING AT ALL"); +EXTERN BOOL_VAR (tessedit_consistent_reps, TRUE, +"Force all rep chars the same"); + +FILE *txt_mapfile = NULL; //reject map +FILE *unlv_file = NULL; //reject map + +/********************************************************************** + * pixels_to_pts + * + * Convert an integer number of pixels to the nearest integer + * number of points. + **********************************************************************/ + +INT32 pixels_to_pts( //convert coords + INT32 pixels, + INT32 pix_res //resolution + ) { + float pts; //converted value + + pts = pixels * 72.0 / pix_res; + return (INT32) (pts + 0.5); //round it +} + + +void output_pass( //Tess output pass //send to api + PAGE_RES_IT &page_res_it, + BOOL8 write_to_shm) { + BLOCK_RES *block_of_last_word; + INT16 block_id; + BOOL8 force_eol; //During output + BLOCK *nextblock; //block of next word + WERD *nextword; //next word + + if (tessedit_write_txt_map) + txt_mapfile = open_outfile (".map"); + if (tessedit_write_unlv) + unlv_file = open_outfile (".unlv"); + page_res_it.restart_page (); + block_of_last_word = NULL; + while (page_res_it.word () != NULL) { + check_debug_pt (page_res_it.word (), 120); + if (tessedit_write_block_separators && + block_of_last_word != page_res_it.block ()) { + block_of_last_word = page_res_it.block (); + if (block_of_last_word->block->text_region () == NULL) { + if (block_of_last_word->block->poly_block () == NULL) + block_id = 1; + else + block_id = + ((WEIRD_BLOCK *) block_of_last_word->block->poly_block ())-> + id_no(); + } + else + block_id = block_of_last_word->block->text_region ()->id_no (); + if (!NO_BLOCK) + fprintf (textfile, "|^~tr%d\n", block_id); + fprintf (txt_mapfile, "|^~tr%d\n", block_id); + } + + force_eol = (tessedit_write_block_separators && + (page_res_it.block () != page_res_it.next_block ())) || + (page_res_it.next_word () == NULL); + + if (page_res_it.next_word () != NULL) + nextword = page_res_it.next_word ()->word; + else + nextword = NULL; + if (page_res_it.next_block () != NULL) + nextblock = page_res_it.next_block ()->block; + else + nextblock = NULL; + //regardless of tilde crunching + write_results (page_res_it, determine_newline_type (page_res_it.word ()->word, page_res_it.block ()->block, nextword, nextblock), force_eol, + write_to_shm); + page_res_it.forward (); + } + if (write_to_shm) + ocr_send_text(FALSE); + if (tessedit_write_block_separators) { + if (!NO_BLOCK) + fprintf (textfile, "|^~tr\n"); + fprintf (txt_mapfile, "|^~tr\n"); + } + if (tessedit_write_txt_map) { + fprintf (txt_mapfile, "\n"); //because txt gets one + #ifdef __UNIX__ + fsync (fileno (txt_mapfile)); + #endif + fclose(txt_mapfile); + } +} + + +/************************************************************************* + * write_results() + * + * All recognition and rejection has now been done. Generate the following: + * .txt file - giving the final best choices with NO highlighting + * .raw file - giving the tesseract top choice output for each word + * .map file - showing how the .txt file has been rejected in the .ep file + * epchoice list - a list of one element per word, containing the text for the + * epaper. Reject strings are inserted. + * inset list - a list of bounding boxes of reject insets - indexed by the + * reject strings in the epchoice text. + *************************************************************************/ + +void write_results( //output a word + PAGE_RES_IT &page_res_it, //full info + char newline_type, //type of newline + BOOL8 force_eol, //override tilde crunch? + BOOL8 write_to_shm //send to api + ) { + //word to do + WERD_RES *word = page_res_it.word (); + WERD_CHOICE *ep_choice; //ep format + STRING repetition_code; + const STRING *wordstr; + const char *text; + int i; + char unrecognised = STRING (unrecognised_char)[0]; + char ep_chars[32]; //Only for unlv_tilde_crunch + int ep_chars_index = 0; + char txt_chs[32]; //Only for unlv_tilde_crunch + char map_chs[32]; //Only for unlv_tilde_crunch + int txt_index = 0; + static BOOL8 tilde_crunch_written = FALSE; + static BOOL8 last_char_was_newline = TRUE; + static BOOL8 last_char_was_tilde = FALSE; + static BOOL8 empty_block = TRUE; + BOOL8 need_reject = FALSE; + char *ptr; //string ptr + PBLOB_IT blob_it; //blobs + + /* if (word->best_choice->string().length() == 0) + { + tprintf("No output: to output\n"); + } + else if (word->best_choice->string()[0]==' ') + { + tprintf("spaceword to output\n"); + } + else if (word->best_choice->string()[0]=='\0') + { + tprintf("null to output\n"); + }*/ + if (word->unlv_crunch_mode != CR_NONE + && !tessedit_zero_kelvin_rejection && !tessedit_word_for_word) { + if ((word->unlv_crunch_mode != CR_DELETE) && + (!tilde_crunch_written || + ((word->unlv_crunch_mode == CR_KEEP_SPACE) && + (word->word->space () > 0) && + !word->word->flag (W_FUZZY_NON) && + !word->word->flag (W_FUZZY_SP)))) { + if (!word->word->flag (W_BOL) && + (word->word->space () > 0) && + !word->word->flag (W_FUZZY_NON) && + !word->word->flag (W_FUZZY_SP)) { + /* Write a space to separate from preceeding good text */ + txt_chs[txt_index] = ' '; + map_chs[txt_index++] = '1'; + ep_chars[ep_chars_index++] = ' '; + last_char_was_tilde = FALSE; + } + need_reject = TRUE; + } + if ((need_reject && !last_char_was_tilde) || (force_eol && empty_block)) { + /* Write a reject char - mark as rejected unless zero_rejection mode */ + last_char_was_tilde = TRUE; + txt_chs[txt_index] = unrecognised; + if (tessedit_zero_rejection || (suspect_level == 0)) { + map_chs[txt_index++] = '1'; + ep_chars[ep_chars_index++] = unrecognised; + } + else { + map_chs[txt_index++] = '0'; + /* + The ep_choice string is a faked reject to allow newdiff to sync the .etx + with the .txt and .map files. + */ + ep_chars[ep_chars_index++] = CTRL_INSET; + //escape code + //dummy reject + ep_chars[ep_chars_index++] = 1; + //dummy reject + ep_chars[ep_chars_index++] = 1; + //type + ep_chars[ep_chars_index++] = 2; + //dummy reject + ep_chars[ep_chars_index++] = 1; + //dummy reject + ep_chars[ep_chars_index++] = 1; + } + tilde_crunch_written = TRUE; + last_char_was_newline = FALSE; + empty_block = FALSE; + } + + if ((word->word->flag (W_EOL) && !last_char_was_newline) || force_eol) { + /* Add a new line output */ + txt_chs[txt_index] = '\n'; + map_chs[txt_index++] = '\n'; + //end line + ep_chars[ep_chars_index++] = newline_type; + + //Cos of the real newline + tilde_crunch_written = FALSE; + last_char_was_newline = TRUE; + last_char_was_tilde = FALSE; + } + txt_chs[txt_index] = '\0'; + map_chs[txt_index] = '\0'; + //xiaofan + if (tessedit_write_output && !NO_BLOCK) + fprintf (textfile, "%s", txt_chs); + + if (tessedit_write_unlv) + fprintf (unlv_file, "%s", txt_chs); + + if (tessedit_write_txt_map) + fprintf (txt_mapfile, "%s", map_chs); + + //terminate string + ep_chars[ep_chars_index] = '\0'; + word->ep_choice = new WERD_CHOICE (ep_chars, 0, 0, NO_PERM); + + if (force_eol) + empty_block = TRUE; + return; + } + + /* NORMAL PROCESSING of non tilde crunched words */ + + tilde_crunch_written = FALSE; + if (newline_type) + last_char_was_newline = TRUE; + else + last_char_was_newline = FALSE; + empty_block = force_eol; //About to write a real word + + if (unlv_tilde_crunching && + last_char_was_tilde && + (word->word->space () == 0) && + !(word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes) && + (word->best_choice->string ()[0] == ' ')) { + /* Prevent adjacent tilde across words - we know that adjacent tildes within + words have been removed */ + ptr = (char *) word->best_choice->string ().string (); + strcpy (ptr, ptr + 1); //shuffle up + word->reject_map.remove_pos (0); + blob_it = word->outword->blob_list (); + delete blob_it.extract (); //get rid of reject blob + } + if (newline_type || + (word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes)) + last_char_was_tilde = FALSE; + else { + if (word->reject_map.length () > 0) { + if (word->best_choice->string ()[word->reject_map.length () - 1] == + ' ') + last_char_was_tilde = TRUE; + else + last_char_was_tilde = FALSE; + } + else if (word->word->space () > 0) + last_char_was_tilde = FALSE; + /* else it is unchanged as there are no output chars */ + } + + ptr = (char *) word->best_choice->string ().string (); + ASSERT_HOST (strlen (ptr) == word->reject_map.length ()); + + if (word->word->flag (W_REP_CHAR) && tessedit_consistent_reps) + ensure_rep_chars_are_consistent(word); + + set_unlv_suspects(word); + check_debug_pt (word, 120); + if (tessedit_rejection_debug) { + tprintf ("Dict word: \"%s\": %d\n", + word->best_choice->string ().string (), + dict_word (word->best_choice->string ().string ())); + } + + if (tessedit_write_unlv) { + write_unlv_text(word); + } + + if (word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes) { + repetition_code = "|^~R"; + repetition_code += get_rep_char (word); + wordstr = &repetition_code; + } + else { + wordstr = &(word->best_choice->string ()); + if (tessedit_zero_rejection) { + /* OVERRIDE ALL REJECTION MECHANISMS - ONLY REJECT TESS FAILURES */ + text = wordstr->string (); + for (i = 0; text[i] != '\0'; i++) { + if (word->reject_map[i].rejected ()) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + if (tessedit_minimal_rejection) { + /* OVERRIDE ALL REJECTION MECHANISMS - ONLY REJECT TESS FAILURES */ + text = wordstr->string (); + for (i = 0; text[i] != '\0'; i++) { + if ((text[i] != ' ') && word->reject_map[i].rejected ()) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + } + + if (write_to_shm) + write_shm_text (word, page_res_it.block ()->block, + page_res_it.row (), *wordstr); + + if (tessedit_write_output) + write_cooked_text (word->word, *wordstr, TRUE, FALSE, textfile); + + if (tessedit_write_raw_output) + write_cooked_text (word->word, word->raw_choice->string (), + TRUE, FALSE, rawfile); + + if (tessedit_write_txt_map) + write_map(txt_mapfile, word); + + ep_choice = make_epaper_choice (word, newline_type); + word->ep_choice = ep_choice; + + character_count += word->best_choice->string ().length (); + word_count++; +} + + +/********************************************************************** + * make_epaper_choice + * + * Construct the epaper text string for a word, using the reject map to + * determine whether each blob should be rejected. + **********************************************************************/ + +WERD_CHOICE *make_epaper_choice( //convert one word + WERD_RES *word, //word to do + char newline_type //type of newline + ) { + INT16 index = 0; //to string + INT16 blobindex; //to word + INT16 prevright = 0; //right of previous blob + INT16 nextleft; //left of next blob + PBLOB *blob; + BOX inset_box; //bounding box + PBLOB_IT blob_it; //blob iterator + char word_string[MAX_PATH]; //converted string + BOOL8 force_total_reject; + char unrecognised = STRING (unrecognised_char)[0]; + + blob_it.set_to_list (word->outword->blob_list ()); + + ASSERT_HOST (word->reject_map.length () == + word->best_choice->string ().length ()); + /* + tprintf( "\"%s\" -> length: %d; blobcount: %d (%d)\n", + word->best_choice->string().string(), + word->best_choice->string().length(), + blob_it.length(), + blob_count( word->outword ) ); + */ + + if (word->best_choice->string ().length () == 0) + force_total_reject = TRUE; + else { + force_total_reject = FALSE; + ASSERT_HOST (blob_it.length () == + word->best_choice->string ().length ()); + } + if (!blob_it.empty ()) { + for (index = 0; index < word->word->space (); index++) + word_string[index] = ' '; //leading blanks + } + /* Why does this generate leading blanks regardless of whether the + word_choice string is empty, when write_cooked_text ony generates leading + blanks when the string is NOT empty???. */ + + if (word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes) { + strcpy (word_string + index, "|^~R"); + index += 4; + word_string[index++] = get_rep_char (word); + } + else { + if (!blob_it.empty ()) + prevright = blob_it.data ()->bounding_box ().left (); + //actually first left + for (blobindex = 0, blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blobindex++, blob_it.forward ()) { + blob = blob_it.data (); + if (word->reject_map[blobindex].accepted ()) { + if (word->best_choice->string ()[blobindex] == ' ') + //but not rejected!! + word_string[index++] = unrecognised; + else + word_string[index++] = + word->best_choice->string ()[blobindex]; + } + else { // start reject + inset_box = blob->bounding_box (); + /* Extend reject box to include rejected neighbours */ + while (!blob_it.at_last () && + (force_total_reject || + (word->reject_map[blobindex + 1].rejected ()))) { + blobindex++; + blob = blob_it.forward (); + //get total box + inset_box += blob->bounding_box (); + } + if (blob_it.at_last ()) + nextleft = inset_box.right (); + else + nextleft = blob_it.data_relative (1)->bounding_box ().left (); + + // tprintf("Making reject from (%d,%d)->(%d,%d)\n", + // inset_box.left(),inset_box.bottom(), + // inset_box.right(),inset_box.top()); + + index += make_reject (&inset_box, prevright, nextleft, + &word->denorm, &word_string[index]); + } + prevright = blob->bounding_box ().right (); + } + } + if (newline_type) + //end line + word_string[index++] = newline_type; + word_string[index] = '\0'; //terminate string + if (strlen (word_string) != index) { + tprintf ("ASSERT ABOUT TO FAIL: %s, index %d len %d\n", + word_string, index, strlen (word_string)); + } + //don't pass any zeros + ASSERT_HOST (strlen (word_string) == index); + return new WERD_CHOICE (word_string, 0, 0, NO_PERM); +} + + +/********************************************************************** + * make_reject + * + * Add the escape code to the string for the reject. + **********************************************************************/ + +INT16 +make_reject ( //make reject code +BOX * inset_box, //bounding box +INT16 prevright, //previous char +INT16 nextleft, //next char +DENORM * denorm, //de-normalizer +char word_string[] //output string +) { + INT16 index; //to string + INT16 xpos; //start of inset + INT16 ypos; + INT16 width; //size of inset + INT16 height; + INT16 left_offset; //shift form prev char + INT16 right_offset; //shift to next char + INT16 baseline_offset; //shift from baseline + INT16 inset_index = 0; //number of inset + INT16 min_chars; //min width estimate + INT16 max_chars; //max width estimate + float x_centre; //centre of box + + index = 0; + x_centre = (inset_box->left () + inset_box->right ()) / 2.0; + left_offset = + (INT16) (denorm->x (inset_box->left ()) - denorm->x (prevright)); + right_offset = + (INT16) (denorm->x (nextleft) - denorm->x (inset_box->right ())); + xpos = (INT16) floor (denorm->x (inset_box->left ())); + width = (INT16) ceil (denorm->x (inset_box->right ())) - xpos; + ypos = (INT16) floor (denorm->y (inset_box->bottom (), x_centre)); + height = (INT16) ceil (denorm->y (inset_box->top (), x_centre)) - ypos; + baseline_offset = ypos - (INT16) denorm->y (bln_baseline_offset, x_centre); + //escape code + word_string[index++] = CTRL_INSET; + min_chars = (INT16) ceil (0.27 * width / denorm->row ()->x_height ()); + max_chars = (INT16) floor (1.8 * width / denorm->row ()->x_height ()); + /* + Ensure min_chars and max_chars are in the range 0..254. This ensures that + we can add 1 to them to avoid putting \0 in a string, and still not exceed + the max value in a byte. + */ + if (min_chars < 0) + min_chars = 0; + if (min_chars > 254) + min_chars = 254; + if (max_chars < min_chars) + max_chars = min_chars; + if (max_chars > 254) + max_chars = 254; + //min chars + word_string[index++] = min_chars + 1; + //max chars + word_string[index++] = max_chars + 1; + word_string[index++] = 2; //type? + //store index + word_string[index++] = inset_index / 255 + 1; + word_string[index++] = inset_index % 255 + 1; + return index; //size of string +} + + +/********************************************************************** + * determine_newline_type + * + * Find whether we have a wrapping or hard newline. + * Return FALSE if not at end of line. + **********************************************************************/ + +char determine_newline_type( //test line ends + WERD *word, //word to do + BLOCK *block, //current block + WERD *next_word, //next word + BLOCK *next_block //block of next word + ) { + INT16 end_gap; //to right edge + INT16 width; //of next word + BOX word_box; //bounding + BOX next_box; //next word + BOX block_box; //block bounding + + if (!word->flag (W_EOL)) + return FALSE; //not end of line + if (next_word == NULL || next_block == NULL || block != next_block) + return CTRL_NEWLINE; + if (next_word->space () > 0) + return CTRL_HARDLINE; //it is tabbed + word_box = word->bounding_box (); + next_box = next_word->bounding_box (); + block_box = block->bounding_box (); + //gap to eol + end_gap = block_box.right () - word_box.right (); + end_gap -= (INT32) block->space (); + width = next_box.right () - next_box.left (); + // tprintf("end_gap=%d-%d=%d, width=%d-%d=%d, nl=%d\n", + // block_box.right(),word_box.right(),end_gap, + // next_box.right(),next_box.left(),width, + // end_gap>width ? CTRL_HARDLINE : CTRL_NEWLINE); + return end_gap > width ? CTRL_HARDLINE : CTRL_NEWLINE; +} + + +/********************************************************************** + * write_cooked_text + * + * Write the cooked text (with bold for pass2 and underline for reject) + * to the given file. + **********************************************************************/ + +void write_cooked_text( //write output + WERD *word, //word to do + const STRING &text, //text to write + BOOL8 acceptable, //good stuff + BOOL8 pass2, //done on pass2 + FILE *fp //file to write + ) { + INT16 index; //blank counter + int status; + static int newaline = 1; + static int havespace = 0; + char buff[512]; + const char *wordstr = text.string (); + int i = 0; + char unrecognised = STRING (unrecognised_char)[0]; + static int old_segs = 0; + BOX mybox; + for (i = 0; wordstr[i] != '\0'; i++) { + if (wordstr[i] == ' ') + buff[i] = unrecognised; + else + buff[i] = wordstr[i]; + } + buff[i] = '\0'; + + if (fp == stdout) { + tprintf ("Cooked=%s, %d segs, acceptable=%d", + buff, num_popped - old_segs, acceptable); + old_segs = num_popped; + return; + } + + if (text.length () > 0) { + for (index = 0; index < word->space (); index++) { + status = fprintf (fp, " "); + havespace = 1; + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Space Errno: %d", errno); + } + if (pass2) { + status = fprintf (fp, BOLD_ON); + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Bold Errno: %d", errno); + } + if (!acceptable) { + status = fprintf (fp, UNDERLINE_ON); + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Underline Errno: %d", errno); + } + + //xiaofan + if (NO_BLOCK && word && strlen (buff)) { + mybox = word->bounding_box (); + if (newaline || !havespace) { + fprintf (fp, " "); + newaline = 0; + } + fprintf (fp, "(%d," INT32FORMAT ",%d," INT32FORMAT ")", + XOFFSET + mybox.left (), + YOFFSET + page_image.get_ysize () - mybox.top (), + XOFFSET + mybox.right (), + YOFFSET + page_image.get_ysize () - mybox.bottom ()); + havespace = 0; + } + + status = fprintf (fp, "%s", buff); + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Word Errno: %d", errno); + if (pass2) { + status = fprintf (fp, BOLD_OFF); + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Bold off Errno: %d", errno); + } + if (!acceptable) { + status = fprintf (fp, UNDERLINE_OFF); + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Underline off Errno: %d", errno); + } + } + if (word->flag (W_EOL)) { + status = fprintf (fp, "\n"); + newaline = 1; + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Newline Errno: %d", errno); + } + status = fflush (fp); + if (status != 0) + WRITEFAILED.error ("write_cooked_text", EXIT, "Fflush Errno: %d", errno); +} + + +/********************************************************************** + * write_shm_text + * + * Write the cooked text to the shared memory for the api. + **********************************************************************/ + +void write_shm_text( //write output + WERD_RES *word, //word to do + BLOCK *block, //block it is from + ROW_RES *row, //row it is from + const STRING &text //text to write + ) { + INT32 index; //char counter + INT32 index2; //char counter + INT32 length; //chars in word + INT32 ptsize; //font size + INT8 blanks; //blanks in word + UINT8 enhancement; //bold etc + UINT8 font; //font index + char unrecognised = STRING (unrecognised_char)[0]; + PBLOB *blob; + BOX blob_box; //bounding box + PBLOB_IT blob_it; //blob iterator + WERD copy_outword; // copy to denorm + UINT32 rating; //of char + BOOL8 lineend; //end of line + + //point size + ptsize = pixels_to_pts ((INT32) (row->row->x_height () + row->row->ascenders () - row->row->descenders ()), 300); + if (word->word->flag (W_BOL) && ocr_char_space () < 128 + && ocr_send_text (TRUE) != OKAY) + return; //release failed + copy_outword = *(word->outword); + copy_outword.baseline_denormalise (&word->denorm); + blob_it.set_to_list (copy_outword.blob_list ()); + length = text.length (); + + if (length > 0) { + blanks = word->word->space (); + if (blanks == 0 && tessedit_word_for_word && !word->word->flag (W_BOL)) + blanks = 1; + for (index = 0; index < length; index++, blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + + enhancement = 0; + if (word->italic > 0 || word->italic == 0 && row->italic > 0) + enhancement |= EUC_ITALIC; + if (word->bold > 0 || word->bold == 0 && row->bold > 0) + enhancement |= EUC_BOLD; + if (tessedit_write_ratings) + rating = (UINT32) (-word->best_choice->certainty () / 0.035); + else if (tessedit_zero_rejection) + rating = text[index] == ' ' ? 100 : 0; + else + rating = word->reject_map[index].accepted ()? 0 : 100; + if (rating > 255) + rating = 255; + if (word->font1_count > 2) + font = word->font1; + else if (row->font1_count > 8) + font = row->font1; + else + //font index + font = word->word->flag (W_DONT_CHOP) ? 0 : 1; + + lineend = word->word->flag (W_EOL) && index == length - 1; + if (word->word->flag (W_EOL) && tessedit_zero_rejection + && index < length - 1 && text[index + 1] == ' ') { + for (index2 = index + 1; index2 < length && text[index2] == ' '; + index2++); + if (index2 == length) + lineend = TRUE; + } + + if (!tessedit_zero_rejection || text[index] != ' ' + || tessedit_word_for_word) { + //confidence + ocr_append_char (text[index] == ' ' ? unrecognised : text[index], blob_box.left (), blob_box.right (), page_image.get_ysize () - 1 - blob_box.top (), page_image.get_ysize () - 1 - blob_box.bottom (), font, (UINT8) rating, + ptsize, //point size + blanks, enhancement, //enhancement + OCR_CDIR_LEFT_RIGHT, + OCR_LDIR_DOWN_RIGHT, + lineend ? OCR_NL_NEWLINE : OCR_NL_NONE); + blanks = 0; + } + + } + } + else if (tessedit_word_for_word) { + blanks = word->word->space (); + if (blanks == 0 && !word->word->flag (W_BOL)) + blanks = 1; + blob_box = word->word->bounding_box (); + + enhancement = 0; + if (word->italic > 0) + enhancement |= EUC_ITALIC; + if (word->bold > 0) + enhancement |= EUC_BOLD; + rating = 100; + if (word->font1_count > 2) + font = word->font1; + else if (row->font1_count > 8) + font = row->font1; + else + //font index + font = word->word->flag (W_DONT_CHOP) ? 0 : 1; + + lineend = word->word->flag (W_EOL); + + //font index + ocr_append_char (unrecognised, blob_box.left (), blob_box.right (), page_image.get_ysize () - 1 - blob_box.top (), page_image.get_ysize () - 1 - blob_box.bottom (), font, + rating, //confidence + ptsize, //point size + blanks, enhancement, //enhancement + OCR_CDIR_LEFT_RIGHT, + OCR_LDIR_DOWN_RIGHT, + lineend ? OCR_NL_NEWLINE : OCR_NL_NONE); + } +} + + +/********************************************************************** + * write_map + * + * Write a map file of 0's and 1'a which associates characters from the .txt + * file with those in the .etx file. 0 = .txt char was deleted. 1 = .txt char + * is kept. Note that there may be reject regions in the .etx file WITHOUT + * .txt chars being rejected. The map file should be the same length, and + * the same number of lines as the .txt file + * + * The paramaterised input is because I thought I might be able to generate + * multiple map files in a single run. However, it didn't work because + * newdiff needs etx files! + **********************************************************************/ + +void write_map( //output a map file + FILE *mapfile, //mapfile to write to + WERD_RES *word) { + INT16 index; + int status; + STRING mapstr = ""; + + if (word->best_choice->string ().length () > 0) { + for (index = 0; index < word->word->space (); index++) { + if (word->reject_spaces && + (suspect_level >= suspect_space_level) && + !tessedit_minimal_rejection && !tessedit_zero_rejection) + /* Write rejected spaces to .map file ONLY. Newdiff converts these back to + accepted spaces AFTER generating basic space stats but BEFORE using .etx */ + status = fprintf (mapfile, "0"); + else + status = fprintf (mapfile, "1"); + if (status < 0) + WRITEFAILED.error ("write_map", EXIT, "Space Errno: %d", errno); + } + + if ((word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes)) { + for (index = 0; index < 5; index++) + mapstr += '1'; + } + else { + ASSERT_HOST (word->reject_map.length () == + word->best_choice->string ().length ()); + + for (index = 0; index < word->reject_map.length (); index++) { + if (word->reject_map[index].accepted ()) + mapstr += '1'; + else + mapstr += '0'; + } + } + status = fprintf (mapfile, "%s", mapstr.string ()); + if (status < 0) + WRITEFAILED.error ("write_map", EXIT, "Map str Errno: %d", errno); + } + if (word->word->flag (W_EOL)) { + status = fprintf (mapfile, "\n"); + if (status < 0) + WRITEFAILED.error ("write_map", EXIT, "Newline Errno: %d", errno); + } + status = fflush (mapfile); + if (status != 0) + WRITEFAILED.error ("write_map", EXIT, "fflush Errno: %d", errno); +} + + +/************************************************************************* + * open_file() + *************************************************************************/ + +FILE *open_outfile( //open .map & .unlv file + const char *extension) { + STRING file_name; + FILE *outfile; + + file_name = imagebasename + extension; + if (!(outfile = fopen (file_name.string (), "w"))) { + CANTOPENFILE.error ("open_outfile", EXIT, "%s %d", + file_name.string (), errno); + } + return outfile; +} + + +void write_unlv_text(WERD_RES *word) { + const char *wordstr; + + char buff[512]; //string to output + int i = 0; + int j = 0; + char unrecognised = STRING (unrecognised_char)[0]; + int status; + char space_str[3]; + + wordstr = word->best_choice->string ().string (); + + /* DONT need to do anything special for repeated char words - at this stage + the repetition char has been identified and any other chars have been + rejected. + */ + + for (; wordstr[i] != '\0'; i++) { + if ((wordstr[i] == ' ') || + (wordstr[i] == '~') || (wordstr[i] == '^') || (wordstr[i] == '|')) + buff[j++] = unrecognised; + else { + if (word->reject_map[i].rejected ()) + buff[j++] = '^'; //Add suspect marker + buff[j++] = wordstr[i]; + } + } + buff[j] = '\0'; + + if (strlen (wordstr) > 0) { + if (word->reject_spaces && + (suspect_level >= suspect_space_level) && + !tessedit_minimal_rejection && !tessedit_zero_rejection) + strcpy (space_str, "^ "); //Suspect space + else + strcpy (space_str, " "); //Certain space + + for (i = 0; i < word->word->space (); i++) { + status = fprintf (unlv_file, "%s", space_str); + if (status < 0) + WRITEFAILED.error ("write_unlv_text", EXIT, + "Space Errno: %d", errno); + } + + status = fprintf (unlv_file, "%s", buff); + if (status < 0) + WRITEFAILED.error ("write_unlv_text", EXIT, "Word Errno: %d", errno); + } + if (word->word->flag (W_EOL)) { + status = fprintf (unlv_file, "\n"); + if (status < 0) + WRITEFAILED.error ("write_unlv_text", EXIT, + "Newline Errno: %d", errno); + } + status = fflush (unlv_file); + if (status != 0) + WRITEFAILED.error ("write_unlv_text", EXIT, "Fflush Errno: %d", errno); +} + + +/************************************************************************* + * get_rep_char() + * Return the first accepted character from the repetition string. This is the + * character which is repeated - as determined earlier by fix_rep_char() + *************************************************************************/ +char get_rep_char( // what char is repeated? + WERD_RES *word) { + int i; + + for (i = 0; + ((i < word->reject_map.length ()) && + (word->reject_map[i].rejected ())); i++); + if (i < word->reject_map.length ()) + return word->best_choice->string ()[i]; + else + return STRING (unrecognised_char)[0]; +} + + +void ensure_rep_chars_are_consistent(WERD_RES *word) { + char rep_char = get_rep_char (word); + char *ptr; + + ptr = (char *) word->best_choice->string ().string (); + for (; *ptr != '\0'; ptr++) { + if (*ptr != rep_char) + *ptr = rep_char; + } +} + + +/************************************************************************* + * SUSPECT LEVELS + * + * 0 - dont reject ANYTHING + * 1,2 - partial rejection + * 3 - BEST + * + * NOTE: to reject JUST tess failures in the .map file set suspect_level 3 and + * tessedit_minimal_rejection. + *************************************************************************/ + +void set_unlv_suspects(WERD_RES *word) { + int len = word->reject_map.length (); + int i; + const char *ptr; + float rating_per_ch; + + ptr = word->best_choice->string ().string (); + + if (suspect_level == 0) { + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected ()) + word->reject_map[i].setrej_minimal_rej_accept (); + } + return; + } + + if (suspect_level >= 3) + return; //Use defaults + + /* NOW FOR LEVELS 1 and 2 Find some stuff to unreject*/ + + if (safe_dict_word (ptr) && (count_alphas (ptr) > suspect_short_words)) { + /* Unreject alphas in dictionary words */ + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected () && isalpha (ptr[i])) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + + rating_per_ch = word->best_choice->rating () / word->reject_map.length (); + + if (rating_per_ch >= suspect_rating_per_ch) + return; //Dont touch bad ratings + + if ((word->tess_accepted) || (rating_per_ch < suspect_accept_rating)) { + /* Unreject any Tess Acceptable word - but NOT tess reject chs*/ + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected () && (ptr[i] != ' ')) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected ()) { + if (word->reject_map[i].flag (R_DOC_REJ)) + word->reject_map[i].setrej_minimal_rej_accept (); + if (word->reject_map[i].flag (R_BLOCK_REJ)) + word->reject_map[i].setrej_minimal_rej_accept (); + if (word->reject_map[i].flag (R_ROW_REJ)) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + + if (suspect_level == 2) + return; + + if (!suspect_constrain_1Il || + (word->reject_map.length () <= suspect_short_words)) { + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected ()) { + if ((word->reject_map[i].flag (R_1IL_CONFLICT) || + word->reject_map[i].flag (R_POSTNN_1IL))) + word->reject_map[i].setrej_minimal_rej_accept (); + + if (!suspect_constrain_1Il && + word->reject_map[i].flag (R_MM_REJECT)) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + } + + if ((acceptable_word_string (word->best_choice->string ().string ()) + != AC_UNACCEPTABLE) || + acceptable_number_string (word->best_choice->string ().string ())) { + if (word->reject_map.length () > suspect_short_words) { + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected () && + (!word->reject_map[i].perm_rejected () || + word->reject_map[i].flag (R_1IL_CONFLICT) || + word->reject_map[i].flag (R_POSTNN_1IL) || + word->reject_map[i].flag (R_MM_REJECT))) { + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + } + } +} + + +INT16 count_alphas( //how many alphas + const char *s) { + int count = 0; + + for (; *s != '\0'; s++) { + if (isalpha (*s)) + count++; + } + return count; +} + + +INT16 count_alphanums( //how many alphanums + const char *s) { + int count = 0; + + for (; *s != '\0'; s++) { + if (isalnum (*s)) + count++; + } + return count; +} + + +BOOL8 acceptable_number_string(const char *s) { + BOOL8 prev_digit = FALSE; + + if (*s == '(') + s++; + + if ((*s == '$') || (*s == '.') || (*s == '+') || (*s == '-')) + s++; + + for (; *s != '\0'; s++) { + if (isdigit (*s)) + prev_digit = TRUE; + else if (prev_digit && ((*s == '.') || (*s == ',') || (*s == '-'))) + prev_digit = FALSE; + else if (prev_digit && + (*(s + 1) == '\0') && ((*s == '%') || (*s == ')'))) + return TRUE; + else if (prev_digit && + (*s == '%') && (*(s + 1) == ')') && (*(s + 2) == '\0')) + return TRUE; + else + return FALSE; + } + return TRUE; +} diff --git a/ccmain/output.h b/ccmain/output.h new file mode 100644 index 0000000000..f644f27b8e --- /dev/null +++ b/ccmain/output.h @@ -0,0 +1,112 @@ +/****************************************************************** + * File: output.h (Formerly output.h) + * Description: Output pass + * Author: Phil Cheatle + * Created: Thu Aug 4 10:56:08 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef OUTPUT_H +#define OUTPUT_H + +#include "varable.h" +//#include "epapconv.h" +#include "pageres.h" +#include "notdll.h" + +extern BOOL_EVAR_H (tessedit_write_block_separators, TRUE, +"Write block separators in output"); +extern BOOL_VAR_H (tessedit_write_raw_output, FALSE, +"Write raw stuff to name.raw"); +extern BOOL_EVAR_H (tessedit_write_output, TRUE, "Write text to name.txt"); +extern BOOL_EVAR_H (tessedit_write_txt_map, TRUE, +"Write .txt to .etx map file"); +extern BOOL_EVAR_H (tessedit_write_rep_codes, TRUE, +"Write repetition char code"); +extern BOOL_EVAR_H (tessedit_write_unlv, FALSE, "Write .unlv output file"); +extern STRING_EVAR_H (unrecognised_char, "|", +"Output char for unidentified blobs"); +extern INT_EVAR_H (suspect_level, 99, "Suspect marker level"); +extern INT_VAR_H (suspect_space_level, 100, +"Min suspect level for rejecting spaces"); +extern INT_VAR_H (suspect_short_words, 2, +"Dont Suspect dict wds longer than this"); +extern BOOL_VAR_H (suspect_constrain_1Il, FALSE, +"UNLV keep 1Il chars rejected"); +extern double_VAR_H (suspect_rating_per_ch, 999.9, +"Dont touch bad rating limit"); +extern double_VAR_H (suspect_accept_rating, -999.9, +"Accept good rating limit"); +extern BOOL_EVAR_H (tessedit_minimal_rejection, FALSE, +"Only reject tess failures"); +extern BOOL_VAR_H (tessedit_zero_rejection, FALSE, "Dont reject ANYTHING"); +extern BOOL_VAR_H (tessedit_word_for_word, FALSE, +"Make output have exactly one word per WERD"); +extern BOOL_VAR_H (tessedit_consistent_reps, TRUE, +"Force all rep chars the same"); +void output_pass( //Tess output pass //send to api + PAGE_RES_IT &page_res_it, + BOOL8 write_to_shm); +void write_results( //output a word + PAGE_RES_IT &page_res_it, //full info + char newline_type, //type of newline + BOOL8 force_eol, //override tilde crunch? + BOOL8 write_to_shm //send to api + ); +WERD_CHOICE *make_epaper_choice( //convert one word + WERD_RES *word, //word to do + char newline_type //type of newline + ); +INT16 make_reject ( //make reject code +BOX * inset_box, //bounding box +INT16 prevright, //previous char +INT16 nextleft, //next char +DENORM * denorm, //de-normalizer +char word_string[] //output string +); +char determine_newline_type( //test line ends + WERD *word, //word to do + BLOCK *block, //current block + WERD *next_word, //next word + BLOCK *next_block //block of next word + ); +void write_cooked_text( //write output + WERD *word, //word to do + const STRING &text, //text to write + BOOL8 acceptable, //good stuff + BOOL8 pass2, //done on pass2 + FILE *fp //file to write + ); +void write_shm_text( //write output + WERD_RES *word, //word to do + BLOCK *block, //block it is from + ROW_RES *row, //row it is from + const STRING &text //text to write + ); +void write_map( //output a map file + FILE *mapfile, //mapfile to write to + WERD_RES *word); +FILE *open_outfile( //open .map & .unlv file + const char *extension); +void write_unlv_text(WERD_RES *word); +char get_rep_char( // what char is repeated? + WERD_RES *word); +void ensure_rep_chars_are_consistent(WERD_RES *word); +void set_unlv_suspects(WERD_RES *word); +INT16 count_alphas( //how many alphas + const char *s); +INT16 count_alphanums( //how many alphanums + const char *s); +BOOL8 acceptable_number_string(const char *s); +#endif diff --git a/ccmain/paircmp.cpp b/ccmain/paircmp.cpp new file mode 100644 index 0000000000..a9443b26ef --- /dev/null +++ b/ccmain/paircmp.cpp @@ -0,0 +1,107 @@ +/********************************************************************** + * File: paircmp.cpp (Formerly paircmp.c) + * Description: Code to compare two blobs using the adaptive matcher + * Author: Ray Smith + * Created: Wed Apr 21 09:31:02 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "blobcmp.h" +#include "tfacep.h" +#include "paircmp.h" + +#define EXTERN + +/********************************************************************** + * compare_blob_pairs + * + * A blob processor to compare pairs of selected blobs. + **********************************************************************/ + +BOOL8 compare_blob_pairs( //blob processor + BLOCK *, + ROW *row, //row it came from + WERD *, + PBLOB *blob //blob to compare + ) { + static ROW *prev_row = NULL; //other in pair + static PBLOB *prev_blob = NULL; + float rating; //from matcher + + if (prev_row == NULL || prev_blob == NULL) { + prev_row = row; + prev_blob = blob; + } + else { + rating = compare_blobs (prev_blob, prev_row, blob, row); + tprintf ("Rating=%g\n", rating); + prev_row = NULL; + prev_blob = NULL; + } + return TRUE; +} + + +/********************************************************************** + * compare_blobs + * + * Compare 2 blobs and return the rating. + **********************************************************************/ + +float compare_blobs( //match 2 blobs + PBLOB *blob1, //first blob + ROW *row1, //row it came from + PBLOB *blob2, //other blob + ROW *row2) { + PBLOB *bn_blob1; //baseline norm + PBLOB *bn_blob2; + DENORM denorm1, denorm2; + float rating; //match result + + bn_blob1 = blob1->baseline_normalise (row1, &denorm1); + bn_blob2 = blob2->baseline_normalise (row2, &denorm2); + rating = compare_bln_blobs (bn_blob1, &denorm1, bn_blob2, &denorm2); + delete bn_blob1; + delete bn_blob2; + return rating; +} + + +/********************************************************************** + * compare_bln_blobs + * + * Compare 2 baseline normalised blobs and return the rating. + **********************************************************************/ + +float compare_bln_blobs( //match 2 blobs + PBLOB *blob1, //first blob + DENORM *denorm1, + PBLOB *blob2, //other blob + DENORM *denorm2) { + TBLOB *tblob1; //tessblobs + TBLOB *tblob2; + TEXTROW tessrow1, tessrow2; //tess rows + float rating; //match result + + tblob1 = make_tess_blob (blob1, TRUE); + make_tess_row(denorm1, &tessrow1); + tblob2 = make_tess_blob (blob2, TRUE); + make_tess_row(denorm2, &tessrow2); + rating = compare_tess_blobs (tblob1, &tessrow1, tblob2, &tessrow2); + free_blob(tblob1); + free_blob(tblob2); + + return rating; +} diff --git a/ccmain/paircmp.h b/ccmain/paircmp.h new file mode 100644 index 0000000000..772cdd8370 --- /dev/null +++ b/ccmain/paircmp.h @@ -0,0 +1,43 @@ +/********************************************************************** + * File: paircmp.h (Formerly paircmp.h) + * Description: Code to compare two blobs using the adaptive matcher + * Author: Ray Smith + * Created: Wed Apr 21 09:31:02 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef PAIRCMP_H +#define PAIRCMP_H + +#include "ocrblock.h" +#include "varable.h" +#include "notdll.h" + +BOOL8 compare_blob_pairs( //blob processor + BLOCK *, + ROW *row, //row it came from + WERD *, + PBLOB *blob //blob to compare + ); +float compare_blobs( //match 2 blobs + PBLOB *blob1, //first blob + ROW *row1, //row it came from + PBLOB *blob2, //other blob + ROW *row2); +float compare_bln_blobs( //match 2 blobs + PBLOB *blob1, //first blob + DENORM *denorm1, + PBLOB *blob2, //other blob + DENORM *denorm2); +#endif diff --git a/ccmain/reject.cpp b/ccmain/reject.cpp new file mode 100644 index 0000000000..808af8e839 --- /dev/null +++ b/ccmain/reject.cpp @@ -0,0 +1,1655 @@ +/********************************************************************** + * File: reject.cpp (Formerly reject.c) + * Description: Rejection functions used in tessedit + * Author: Phil Cheatle + * Created: Wed Sep 23 16:50:21 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "tessvars.h" +#ifdef __UNIX__ +#include +#include +#endif +#include "scanutils.h" +#include +#include +//#include "tessbox.h" +#include "memry.h" +#include "reject.h" +#include "tfacep.h" +#include "mainblk.h" +#include "charcut.h" +#include "imgs.h" +#include "scaleimg.h" +#include "control.h" +#include "docqual.h" +#include "secname.h" + +/* #define SECURE_NAMES done in secnames.h when necessary */ + +//extern "C" { +#include "callnet.h" +//} + +#include "notdll.h" + +CLISTIZEH (STRING) CLISTIZE (STRING) +#define EXTERN +EXTERN +INT_VAR (tessedit_reject_mode, 0, "Rejection algorithm"); +EXTERN +INT_VAR (tessedit_ok_mode, 5, "Acceptance decision algorithm"); +EXTERN +BOOL_VAR (tessedit_use_nn, FALSE, ""); +EXTERN +BOOL_VAR (tessedit_rejection_debug, FALSE, "Adaption debug"); +EXTERN +BOOL_VAR (tessedit_rejection_stats, FALSE, "Show NN stats"); +EXTERN +BOOL_VAR (tessedit_flip_0O, TRUE, "Contextual 0O O0 flips"); +EXTERN +double_VAR (tessedit_lower_flip_hyphen, 1.5, +"Aspect ratio dot/hyphen test"); +EXTERN +double_VAR (tessedit_upper_flip_hyphen, 1.8, +"Aspect ratio dot/hyphen test"); + +EXTERN +BOOL_VAR (rej_trust_doc_dawg, FALSE, +"Use DOC dawg in 11l conf. detector"); +EXTERN +BOOL_VAR (rej_1Il_use_dict_word, FALSE, "Use dictword test"); +EXTERN +BOOL_VAR (rej_1Il_trust_permuter_type, TRUE, "Dont double check"); + +EXTERN +BOOL_VAR (one_ell_conflict_default, TRUE, "one_ell_conflict default"); +EXTERN +BOOL_VAR (show_char_clipping, FALSE, "Show clip image window?"); +EXTERN +BOOL_VAR (nn_debug, FALSE, "NN DEBUGGING?"); +EXTERN +BOOL_VAR (nn_reject_debug, FALSE, "NN DEBUG each char?"); +EXTERN +BOOL_VAR (nn_lax, FALSE, "Use 2nd rate matches"); +EXTERN +BOOL_VAR (nn_double_check_dict, FALSE, "Double check"); +EXTERN +BOOL_VAR (nn_conf_double_check_dict, TRUE, +"Double check for confusions"); +EXTERN +BOOL_VAR (nn_conf_1Il, TRUE, "NN use 1Il conflicts"); +EXTERN +BOOL_VAR (nn_conf_Ss, TRUE, "NN use Ss conflicts"); +EXTERN +BOOL_VAR (nn_conf_hyphen, TRUE, "NN hyphen conflicts"); +EXTERN +BOOL_VAR (nn_conf_test_good_qual, FALSE, "NN dodgy 1Il cross check"); +EXTERN +BOOL_VAR (nn_conf_test_dict, TRUE, "NN dodgy 1Il cross check"); +EXTERN +BOOL_VAR (nn_conf_test_sensible, TRUE, "NN dodgy 1Il cross check"); +EXTERN +BOOL_VAR (nn_conf_strict_on_dodgy_chs, TRUE, +"Require stronger NN match"); +EXTERN +double_VAR (nn_dodgy_char_threshold, 0.99, "min accept score"); +EXTERN +INT_VAR (nn_conf_accept_level, 4, "NN accept dodgy 1Il matches? "); +EXTERN +INT_VAR (nn_conf_initial_i_level, 3, +"NN accept initial Ii match level "); + +EXTERN +BOOL_VAR (no_unrej_dubious_chars, TRUE, "Dubious chars next to reject?"); +EXTERN +BOOL_VAR (no_unrej_no_alphanum_wds, TRUE, "Stop unrej of non A/N wds?"); +EXTERN +BOOL_VAR (no_unrej_1Il, FALSE, "Stop unrej of 1Ilchars?"); +EXTERN +BOOL_VAR (rej_use_tess_accepted, TRUE, "Individual rejection control"); +EXTERN +BOOL_VAR (rej_use_tess_blanks, TRUE, "Individual rejection control"); +EXTERN +BOOL_VAR (rej_use_good_perm, TRUE, "Individual rejection control"); +EXTERN +BOOL_VAR (rej_use_sensible_wd, FALSE, "Extend permuter check"); +EXTERN +BOOL_VAR (rej_alphas_in_number_perm, FALSE, "Extend permuter check"); + +EXTERN +double_VAR (rej_whole_of_mostly_reject_word_fract, 0.85, +"if >this fract"); +EXTERN +INT_VAR (rej_mostly_reject_mode, 1, +"0-never, 1-afterNN, 2-after new xht"); +EXTERN +double_VAR (tessed_fullstop_aspect_ratio, 1.2, +"if >this fract then reject"); + +EXTERN +INT_VAR (net_image_width, 40, "NN input image width"); +EXTERN +INT_VAR (net_image_height, 36, "NN input image height"); +EXTERN +INT_VAR (net_image_x_height, 22, "NN input image x_height"); +EXTERN +INT_VAR (tessedit_image_border, 2, "Rej blbs near image edge limit"); + +/* + Net input is assumed to have (net_image_width * net_image_height) input + units of image pixels, followed by 0, 1, or N units representing the + baseline position. 0 implies no baseline information. 1 implies a floating + point value. N implies a "guage" of N units. For any char an initial set + of these are ON, the remainder OFF to indicate the "level" of the + baseline. + + HOWEVER!!! NOTE THAT EACH NEW INPUT LAYER FORMAT EXPECTS TO BE RUN WITH A + DIFFERENT tessed/netmatch/nmatch.c MODULE. - These are classic C modules + generated by aspirin with HARD CODED CONSTANTS +*/ + +EXTERN +INT_VAR (net_bl_nodes, 20, "Number of baseline nodes"); + +EXTERN +double_VAR (nn_reject_threshold, 0.5, "NN min accept score"); +EXTERN +double_VAR (nn_reject_head_and_shoulders, 0.6, "top scores sep factor"); + +/* NOTE - ctoh doesn't handle "=" properly, hence \075 */ +EXTERN +STRING_VAR (ok_single_ch_non_alphanum_wds, "-?\075", +"Allow NN to unrej"); +EXTERN +STRING_VAR (ok_repeated_ch_non_alphanum_wds, "-?*\075", +"Allow NN to unrej"); +EXTERN +STRING_VAR (conflict_set_I_l_1, "Il1[]", "Il1 conflict set"); +EXTERN +STRING_VAR (conflict_set_S_s, "Ss$", "Ss conflict set"); +EXTERN +STRING_VAR (conflict_set_hyphen, "-_~", "hyphen conflict set"); +EXTERN +STRING_VAR (dubious_chars_left_of_reject, "!'+`()-./\\<>;:^_,~\"", +"Unreliable chars"); +EXTERN +STRING_VAR (dubious_chars_right_of_reject, "!'+`()-./\\<>;:^_,~\"", +"Unreliable chars"); + +EXTERN +INT_VAR (min_sane_x_ht_pixels, 8, "Reject any x-ht lt or eq than this"); + +/************************************************************************* + * set_done() + * + * Set the done flag based on the word acceptability criteria + *************************************************************************/ + +void set_done( //set done flag + WERD_RES *word, + INT16 pass) { + /* + 0: Original heuristic used in Tesseract and Ray's prototype Resaljet + */ + if (tessedit_ok_mode == 0) { + /* NOTE - done even if word contains some or all spaces !!! */ + word->done = word->tess_accepted; + } + /* + 1: Reject words containing blanks and on pass 1 reject I/l/1 conflicts + */ + else if (tessedit_ok_mode == 1) { + word->done = word->tess_accepted && + (strchr (word->best_choice->string ().string (), ' ') == NULL); + + if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) + word->done = FALSE; + } + /* + 2: as 1 + only accept dict words or numerics in pass 1 + */ + else if (tessedit_ok_mode == 2) { + word->done = word->tess_accepted && + (strchr (word->best_choice->string ().string (), ' ') == NULL); + + if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) + word->done = FALSE; + + if (word->done && + (pass == 1) && + (word->best_choice->permuter () != SYSTEM_DAWG_PERM) && + (word->best_choice->permuter () != FREQ_DAWG_PERM) && + (word->best_choice->permuter () != USER_DAWG_PERM) && + (word->best_choice->permuter () != NUMBER_PERM)) { + #ifndef SECURE_NAMES + if (tessedit_rejection_debug) + tprintf ("\nVETO Tess accepting poor word \"%s\"\n", + word->best_choice->string ().string ()); + #endif + word->done = FALSE; + } + } + /* + 3: as 2 + only accept dict words or numerics in pass 2 as well + */ + else if (tessedit_ok_mode == 3) { + word->done = word->tess_accepted && + (strchr (word->best_choice->string ().string (), ' ') == NULL); + + if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) + word->done = FALSE; + + if (word->done && + (word->best_choice->permuter () != SYSTEM_DAWG_PERM) && + (word->best_choice->permuter () != FREQ_DAWG_PERM) && + (word->best_choice->permuter () != USER_DAWG_PERM) && + (word->best_choice->permuter () != NUMBER_PERM)) { + #ifndef SECURE_NAMES + if (tessedit_rejection_debug) + tprintf ("\nVETO Tess accepting poor word \"%s\"\n", + word->best_choice->string ().string ()); + #endif + word->done = FALSE; + } + } + /* + 4: as 2 + reject dict ambigs in pass 1 + */ + else if (tessedit_ok_mode == 4) { + word->done = word->tess_accepted && + (strchr (word->best_choice->string ().string (), ' ') == NULL); + + if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) + word->done = FALSE; + + if (word->done && + (pass == 1) && + ((word->best_choice->permuter () != SYSTEM_DAWG_PERM) && + (word->best_choice->permuter () != FREQ_DAWG_PERM) && + (word->best_choice->permuter () != USER_DAWG_PERM) && + (word->best_choice->permuter () != NUMBER_PERM)) || + (test_ambig_word (word))) { + #ifndef SECURE_NAMES + if (tessedit_rejection_debug) + tprintf ("\nVETO Tess accepting poor word \"%s\"\n", + word->best_choice->string ().string ()); + #endif + word->done = FALSE; + } + } + /* + 5: as 3 + reject dict ambigs in both passes + */ + else if (tessedit_ok_mode == 5) { + word->done = word->tess_accepted && + (strchr (word->best_choice->string ().string (), ' ') == NULL); + + if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) + word->done = FALSE; + + if (word->done && + ((word->best_choice->permuter () != SYSTEM_DAWG_PERM) && + (word->best_choice->permuter () != FREQ_DAWG_PERM) && + (word->best_choice->permuter () != USER_DAWG_PERM) && + (word->best_choice->permuter () != NUMBER_PERM)) || + (test_ambig_word (word))) { + #ifndef SECURE_NAMES + if (tessedit_rejection_debug) + tprintf ("\nVETO Tess accepting poor word \"%s\"\n", + word->best_choice->string ().string ()); + #endif + word->done = FALSE; + } + } + + else { + tprintf ("BAD tessedit_ok_mode\n"); + err_exit(); + } +} + + +/************************************************************************* + * make_reject_map() + * + * Sets the done flag to indicate whether the resylt is acceptable. + * + * Sets a reject map for the word. + *************************************************************************/ + +void make_reject_map( //make rej map for wd //detailed results + WERD_RES *word, + BLOB_CHOICE_LIST_CLIST *blob_choices, + ROW *row, + INT16 pass //1st or 2nd? + ) { + INT16 i; + + flip_0O(word); + check_debug_pt (word, -1); //For trap only + set_done(word, pass); //Set acceptance + word->reject_map.initialise (word->best_choice->string ().length ()); + reject_blanks(word); + /* + 0: Rays original heuristic - the baseline + */ + if (tessedit_reject_mode == 0) { + if (!word->done) + reject_poor_matches(word, blob_choices); + } + /* + 5: Reject I/1/l from words where there is no strong contextual confirmation; + the whole of any unacceptable words (incl PERM rej of dubious 1/I/ls); + and the whole of any words which are very small + */ + else if (tessedit_reject_mode == 5) { + if (bln_x_height / word->denorm.scale () <= min_sane_x_ht_pixels) + word->reject_map.rej_word_small_xht (); + else { + one_ell_conflict(word, TRUE); + /* + Originally the code here just used the done flag. Now I have duplicated + and unpacked the conditions for setting the done flag so that each + mechanism can be turned on or off independently. This works WITHOUT + affecting the done flag setting. + */ + if (rej_use_tess_accepted && !word->tess_accepted) + word->reject_map.rej_word_not_tess_accepted (); + + if (rej_use_tess_blanks && + (strchr (word->best_choice->string ().string (), ' ') != NULL)) + word->reject_map.rej_word_contains_blanks (); + + if (rej_use_good_perm) { + if (((word->best_choice->permuter () == SYSTEM_DAWG_PERM) || + (word->best_choice->permuter () == FREQ_DAWG_PERM) || + (word->best_choice->permuter () == USER_DAWG_PERM)) && + (!rej_use_sensible_wd || + (acceptable_word_string + (word->best_choice->string ().string ()) != + AC_UNACCEPTABLE))) { + //PASSED TEST + } + else if (word->best_choice->permuter () == NUMBER_PERM) { + if (rej_alphas_in_number_perm) { + for (i = 0; word->best_choice->string ()[i] != '\0'; + i++) { + if (word->reject_map[i].accepted () && + isalpha (word->best_choice->string ()[i])) + word->reject_map[i].setrej_bad_permuter (); + //rej alpha + } + } + } + else { + word->reject_map.rej_word_bad_permuter (); + } + } + + /* Ambig word rejection was here once !!*/ + + } + } + else { + tprintf ("BAD tessedit_reject_mode\n"); + err_exit(); + } + + if (tessedit_image_border > -1) + reject_edge_blobs(word); + + check_debug_pt (word, 10); + if (tessedit_rejection_debug) { + tprintf ("Permuter Type = %d\n", word->best_choice->permuter ()); + tprintf ("Certainty: %f Rating: %f\n", + word->best_choice->certainty (), word->best_choice->rating ()); + tprintf ("Dict word: %d\n", + dict_word (word->best_choice->string ().string ())); + } + + /* Un-reject any rejected characters if NN permits */ + + if (tessedit_use_nn && (pass == 2) && + word->reject_map.recoverable_rejects ()) + nn_recover_rejects(word, row); + flip_hyphens(word); + check_debug_pt (word, 20); +} + + +void reject_blanks(WERD_RES *word) { + INT16 i; + + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (word->best_choice->string ()[i] == ' ') + //rej unrecognised blobs + word->reject_map[i].setrej_tess_failure (); + } +} + + +void reject_I_1_L(WERD_RES *word) { + INT16 i; + + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (STRING (conflict_set_I_l_1). + contains (word->best_choice->string ()[i])) { + //rej 1Il conflict + word->reject_map[i].setrej_1Il_conflict (); + } + } +} + + +void reject_poor_matches( //detailed results + WERD_RES *word, + BLOB_CHOICE_LIST_CLIST *blob_choices) { + float threshold; + INT16 i = 0; + //super iterator + BLOB_CHOICE_LIST_C_IT list_it = blob_choices; + BLOB_CHOICE_IT choice_it; //real iterator + + #ifndef SECURE_NAMES + if (strlen (word->best_choice->string ().string ()) != list_it.length ()) { + tprintf + ("ASSERT FAIL string:\"%s\"; strlen=%d; choices len=%d; blob len=%d\n", + word->best_choice->string ().string (), + strlen (word->best_choice->string ().string ()), list_it.length (), + word->outword->blob_list ()->length ()); + } + #endif + ASSERT_HOST (strlen (word->best_choice->string ().string ()) == + list_it.length ()); + ASSERT_HOST (word->outword->blob_list ()->length () == list_it.length ()); + threshold = compute_reject_threshold (blob_choices); + + for (list_it.mark_cycle_pt (); + !list_it.cycled_list (); list_it.forward (), i++) { + /* NB - only compares the threshold against the TOP choice char in the + choices list for a blob !! - the selected one may be below the threshold */ + choice_it.set_to_list (list_it.data ()); + if ((word->best_choice->string ()[i] == ' ') || + (choice_it.length () == 0)) + //rej unrecognised blobs + word->reject_map[i].setrej_tess_failure (); + else if (choice_it.data ()->certainty () < threshold) + //rej poor score blob + word->reject_map[i].setrej_poor_match (); + } +} + + +/********************************************************************** + * compute_reject_threshold + * + * Set a rejection threshold for this word. + * Initially this is a trivial function which looks for the largest + * gap in the certainty value. + **********************************************************************/ + +float compute_reject_threshold( //compute threshold //detailed results + BLOB_CHOICE_LIST_CLIST *blob_choices) { + INT16 index; //to ratings + INT16 blob_count; //no of blobs in word + INT16 ok_blob_count = 0; //non TESS rej blobs in word + float *ratings; //array of confidences + float threshold; //rejection threshold + float bestgap; //biggest gap + float gapstart; //bottom of gap + //super iterator + BLOB_CHOICE_LIST_C_IT list_it = blob_choices; + BLOB_CHOICE_IT choice_it; //real iterator + + blob_count = blob_choices->length (); + ratings = (float *) alloc_mem (blob_count * sizeof (float)); + for (list_it.mark_cycle_pt (), index = 0; + !list_it.cycled_list (); list_it.forward (), index++) { + choice_it.set_to_list (list_it.data ()); + if (choice_it.length () > 0) { + ratings[ok_blob_count] = choice_it.data ()->certainty (); + //get in an array + // tprintf("Rating[%d]=%c %g %g\n", + // index,choice_it.data()->char_class(), + // choice_it.data()->rating(),choice_it.data()->certainty()); + ok_blob_count++; + } + } + ASSERT_HOST (index == blob_count); + qsort (ratings, ok_blob_count, sizeof (float), sort_floats); + //sort them + bestgap = 0; + gapstart = ratings[0] - 1; //all reject if none better + if (ok_blob_count >= 3) { + for (index = 0; index < ok_blob_count - 1; index++) { + if (ratings[index + 1] - ratings[index] > bestgap) { + bestgap = ratings[index + 1] - ratings[index]; + //find biggest + gapstart = ratings[index]; + } + } + } + threshold = gapstart + bestgap / 2; + // tprintf("First=%g, last=%g, gap=%g, threshold=%g\n", + // ratings[0],ratings[index],bestgap,threshold); + + free_mem(ratings); + return threshold; +} + + +/********************************************************************** + * sort_floats + * + * qsort function to sort 2 floats. + **********************************************************************/ + +int sort_floats( //qsort function + const void *arg1, //ptrs to floats + const void *arg2) { + float diff; //difference + + diff = *((float *) arg1) - *((float *) arg2); + if (diff > 0) + return 1; + else if (diff < 0) + return -1; + else + return 0; +} + + +/************************************************************************* + * reject_edge_blobs() + * + * If the word is perilously close to the edge of the image, reject those blobs + * in the word which are too close to the edge as they could be clipped. + *************************************************************************/ + +void reject_edge_blobs(WERD_RES *word) { + BOX word_box = word->word->bounding_box (); + BOX blob_box; + PBLOB_IT blob_it = word->outword->blob_list (); + //blobs + int blobindex = 0; + float centre; + + if ((word_box.left () < tessedit_image_border) || + (word_box.bottom () < tessedit_image_border) || + (word_box.right () + tessedit_image_border > + page_image.get_xsize () - 1) || + (word_box.top () + tessedit_image_border > page_image.get_ysize () - 1)) { + ASSERT_HOST (word->reject_map.length () == blob_it.length ()); + for (blobindex = 0, blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blobindex++, blob_it.forward ()) { + blob_box = blob_it.data ()->bounding_box (); + centre = (blob_box.left () + blob_box.right ()) / 2.0; + if ((word->denorm.x (blob_box.left ()) < tessedit_image_border) || + (word->denorm.y (blob_box.bottom (), centre) < + tessedit_image_border) || + (word->denorm.x (blob_box.right ()) + tessedit_image_border > + page_image.get_xsize () - 1) || + (word->denorm.y (blob_box.top (), centre) + + tessedit_image_border > page_image.get_ysize () - 1)) { + word->reject_map[blobindex].setrej_edge_char (); + //close to edge + } + } + } +} + + +/********************************************************************** + * one_ell_conflict() + * + * Identify words where there is a potential I/l/1 error. + * - A bundle of contextual heuristics! + **********************************************************************/ + +BOOL8 one_ell_conflict(WERD_RES *word_res, BOOL8 update_map) { + const char *word; + INT16 word_len; //its length + INT16 first_alphanum_idx; + INT16 i; + BOOL8 non_conflict_set_char; //non conf set a/n? + BOOL8 conflict = FALSE; + BOOL8 allow_1s; + ACCEPTABLE_WERD_TYPE word_type; + BOOL8 dict_perm_type; + BOOL8 dict_word_ok; + int dict_word_type; + + word = word_res->best_choice->string ().string (); + word_len = strlen (word); + /* + If there are no occurrences of the conflict set characters then the word + is OK. + */ + if (strpbrk (word, conflict_set_I_l_1.string ()) == NULL) + return FALSE; + + /* + There is a conflict if there are NO other (confirmed) alphanumerics apart + from those in the conflict set. + */ + + for (i = 0, non_conflict_set_char = FALSE; + (i < word_len) && !non_conflict_set_char; i++) + non_conflict_set_char = isalnum (word[i]) && + !STRING (conflict_set_I_l_1).contains (word[i]); + if (!non_conflict_set_char) { + if (update_map) + reject_I_1_L(word_res); + return TRUE; + } + + /* + If the word is accepted by a dawg permuter, and the first alpha character + is "I" or "l", check to see if the alternative is also a dawg word. If it + is, then there is a potential error otherwise the word is ok. + */ + + dict_perm_type = (word_res->best_choice->permuter () == SYSTEM_DAWG_PERM) || + (word_res->best_choice->permuter () == USER_DAWG_PERM) || + (rej_trust_doc_dawg && + (word_res->best_choice->permuter () == DOC_DAWG_PERM)) || + (word_res->best_choice->permuter () == FREQ_DAWG_PERM); + dict_word_type = dict_word (word); + dict_word_ok = (dict_word_type > 0) && + (rej_trust_doc_dawg || (dict_word_type != DOC_DAWG_PERM)); + + if ((rej_1Il_use_dict_word && dict_word_ok) || + (rej_1Il_trust_permuter_type && dict_perm_type) || + (dict_perm_type && dict_word_ok)) { + first_alphanum_idx = first_alphanum_pos (word); + if (word[first_alphanum_idx] == 'I') { + word_res->best_choice->string ()[first_alphanum_idx] = 'l'; + if (safe_dict_word (word) > 0) { + word_res->best_choice->string ()[first_alphanum_idx] = 'I'; + if (update_map) + word_res->reject_map[first_alphanum_idx]. + setrej_1Il_conflict(); + return TRUE; + } + else { + word_res->best_choice->string ()[first_alphanum_idx] = 'I'; + return FALSE; + } + } + + if (word[first_alphanum_idx] == 'l') { + word_res->best_choice->string ()[first_alphanum_idx] = 'I'; + if (safe_dict_word (word) > 0) { + word_res->best_choice->string ()[first_alphanum_idx] = 'l'; + if (update_map) + word_res->reject_map[first_alphanum_idx]. + setrej_1Il_conflict(); + return TRUE; + } + else { + word_res->best_choice->string ()[first_alphanum_idx] = 'l'; + return FALSE; + } + } + return FALSE; + } + + /* + NEW 1Il code. The old code relied on permuter types too much. In fact, + tess will use TOP_CHOICE permute for good things like "palette". + In this code the string is examined independently to see if it looks like + a well formed word. + */ + + /* + REGARDLESS OF PERMUTER, see if flipping a leading I/l generates a + dictionary word. + */ + first_alphanum_idx = first_alphanum_pos (word); + if (word[first_alphanum_idx] == 'l') { + word_res->best_choice->string ()[first_alphanum_idx] = 'I'; + if (safe_dict_word (word) > 0) + return FALSE; + else + word_res->best_choice->string ()[first_alphanum_idx] = 'l'; + } + else if (word[first_alphanum_idx] == 'I') { + word_res->best_choice->string ()[first_alphanum_idx] = 'l'; + if (safe_dict_word (word) > 0) + return FALSE; + else + word_res->best_choice->string ()[first_alphanum_idx] = 'I'; + } + /* + For strings containing digits: + If there are no alphas OR the numeric permuter liked the word, + reject any non 1 conflict chs + Else reject all conflict chs + */ + if (word_contains_non_1_digit (word)) { + allow_1s = (alpha_count (word) == 0) || + (word_res->best_choice->permuter () == NUMBER_PERM); + + conflict = FALSE; + for (i = 0; i < word_len; i++) { + if ((!allow_1s || (word[i] != '1')) && + STRING (conflict_set_I_l_1).contains (word[i])) { + if (update_map) + word_res->reject_map[i].setrej_1Il_conflict (); + conflict = TRUE; + } + } + return conflict; + } + /* + For anything else. See if it conforms to an acceptable word type. If so, + treat accordingly. + */ + word_type = acceptable_word_string (word); + if ((word_type == AC_LOWER_CASE) || (word_type == AC_INITIAL_CAP)) { + first_alphanum_idx = first_alphanum_pos (word); + if (STRING (conflict_set_I_l_1).contains (word[first_alphanum_idx])) { + if (update_map) + word_res->reject_map[first_alphanum_idx].setrej_1Il_conflict (); + return TRUE; + } + else + return FALSE; + } + else if (word_type == AC_UPPER_CASE) { + return FALSE; + } + else { + if (update_map) + reject_I_1_L(word_res); + return TRUE; + } +} + + +INT16 first_alphanum_pos(const char *word) { + INT16 i; + + for (i = 0; word[i] != '\0'; i++) { + if (isalnum (word[i])) + return i; + } + return -1; +} + + +INT16 alpha_count(const char *word) { + INT16 i; + INT16 count = 0; + + for (i = 0; word[i] != '\0'; i++) { + if (isalpha (word[i])) + count++; + } + return count; +} + + +BOOL8 word_contains_non_1_digit(const char *word) { + INT16 i; + + for (i = 0; word[i] != '\0'; i++) { + if (isdigit (word[i]) && word[i] != '1') + return TRUE; + } + return FALSE; +} + + +BOOL8 test_ambig_word( //test for ambiguity + WERD_RES *word) { + BOOL8 ambig = FALSE; + + if ((word->best_choice->permuter () == SYSTEM_DAWG_PERM) || + (word->best_choice->permuter () == FREQ_DAWG_PERM) || + (word->best_choice->permuter () == USER_DAWG_PERM)) { + ambig = !NoDangerousAmbig(word->best_choice->string().string(), NULL); + } + return ambig; +} + + +/************************************************************************* + * ambig_word() + * + * This is a recursive routine which tests the dictionary for all combinations + * of conflict set alternatives for characters in a given word. + *************************************************************************/ + +BOOL8 ambig_word( //original word + const char *start_word, + char *temp_word, //alterable copy + INT16 test_char_pos //idx to char to alter + ) { + const char *ambigs; //Ambiguities for char + + if (*(temp_word + test_char_pos) == '\0') { + if (safe_dict_word (temp_word)) { + if (strcmp (start_word, temp_word) == 0) + return FALSE; + else + return TRUE; + } + else + return FALSE; + } + else { + ambigs = char_ambiguities (*(temp_word + test_char_pos)); + if (ambigs == NULL) + return ambig_word (start_word, temp_word, test_char_pos + 1); + else { + while (*ambigs != '\0') { + *(temp_word + test_char_pos) = *ambigs++; + //test next ambiguity + if (ambig_word (start_word, temp_word, test_char_pos + 1)) + return TRUE; + } + return FALSE; + } + } +} + + +/************************************************************************* + * char_ambiguities() + * + * Return a pointer to a string containing the full conflict set of characters + * which includes the specified character, if there is one. If the specified + * character is not a member of a conflict set, return NULL. + * (NOTE that a character is assumed to be a member of only ONE conflict set.) + *************************************************************************/ + +const char *char_ambiguities(char c) { + static STRING_CLIST conflict_sets; + static BOOL8 read_conflict_sets = FALSE; + STRING_C_IT cs_it(&conflict_sets); + const char *cs; + STRING cs_file_name; + FILE *cs_file; + char buff[1024]; + + if (!read_conflict_sets) { + cs_file_name = datadir + "confsets"; + if (!(cs_file = fopen (cs_file_name.string (), "r"))) { + CANTOPENFILE.error ("char_ambiguities", EXIT, "%s %d", + cs_file_name.string (), errno); + } + while (fscanf (cs_file, "%s", buff) == 1) { + cs_it.add_after_then_move (new STRING (buff)); + } + read_conflict_sets = TRUE; + cs_it.move_to_first (); + if (tessedit_rejection_debug) { + for (cs_it.mark_cycle_pt (); + !cs_it.cycled_list (); cs_it.forward ()) { + tprintf ("\"%s\"\n", cs_it.data ()->string ()); + } + } + } + + cs_it.move_to_first (); + for (cs_it.mark_cycle_pt (); !cs_it.cycled_list (); cs_it.forward ()) { + cs = cs_it.data ()->string (); + if (strchr (cs, c) != NULL) + return cs; + } + return NULL; +} + +#ifndef EMBEDDED +void test_ambigs(const char *word) { + char orig_word[80]; + char temp_word[80]; + + if (strlen (word) > 80) + tprintf ("Ridiculously long word \"%s\"\n", word); + else { + strcpy(orig_word, word); + while (strlen (orig_word) > 0) { + strcpy(temp_word, orig_word); + + #ifndef SECURE_NAMES + if (ambig_word (orig_word, temp_word, 0)) + tprintf ("Ambiguity \"%s\" -> \"%s\"\n", orig_word, temp_word); + else + tprintf ("NO Ambiguities for \"%s\"\n", orig_word); + tprintf ("Next Word > "); + #endif + scanf ("%s", orig_word); + } + } +} +#endif + +/************************************************************************* + * nn_recover_rejects() + * Generate the nn_reject_map - a copy of the current reject map, but dont + * reject previously rejected chars if the NN matcher agrees with the best + * choice. + *************************************************************************/ + +void nn_recover_rejects(WERD_RES *word, ROW *row) { + //copy for debug + REJMAP old_map = word->reject_map; + /* + NOTE THAT THIS IS RELATIVELY INEFFICIENT AS THE WHOLE OF THE WERD IS + MATCHED BY THE NN MATCHER. IF COULD EASILY BE RESTRICTED TO JUST THE + REJECT CHARACTERS (Though initial use is when words are total rejects + anyway). + */ + + set_global_subsubloc_code(SUBSUBLOC_NN); + nn_match_word(word, row); + + if (no_unrej_1Il) + dont_allow_1Il(word); + if (no_unrej_dubious_chars) + dont_allow_dubious_chars(word); + + if (rej_mostly_reject_mode == 1) + reject_mostly_rejects(word); + /* + IF there are no unrejected alphanumerics AND + The word is not an acceptable single non alphanum char word AND + The word is not an acceptable repeated non alphanum char word + THEN Reject whole word + */ + if (no_unrej_no_alphanum_wds && + (count_alphanums (word) < 1) && + !((word->best_choice->string ().length () == 1) && + STRING (ok_single_ch_non_alphanum_wds).contains (word->best_choice-> + string ()[0])) + && !repeated_nonalphanum_wd (word, row)) + + word->reject_map.rej_word_no_alphanums (); + + #ifndef SECURE_NAMES + + if (nn_debug) { + tprintf ("\nTess: \"%s\" MAP ", word->best_choice->string ().string ()); + old_map.print (stdout); + tprintf ("->"); + word->reject_map.print (stdout); + tprintf ("\n"); + } + #endif + set_global_subsubloc_code(SUBSUBLOC_OTHER); +} + + +void nn_match_word( //Match a word + WERD_RES *word, + ROW *row) { + PIXROW_LIST *pixrow_list; + PIXROW_IT pixrow_it; + IMAGELINE *imlines; //lines of the image + BOX pix_box; //box of imlines extent +#ifndef GRAPHICS_DISABLED + WINDOW win = NULL; +#endif + IMAGE clip_image; + IMAGE scaled_image; + float baseline_pos; + INT16 net_image_size; + INT16 clip_image_size; + WERD copy_outword; // copy to denorm + INT16 i; + + const char *word_string; + BOOL8 word_in_dict; //Tess wd in dict + BOOL8 checked_dict_word; //Tess wd definitely in dict + BOOL8 sensible_word; //OK char string + BOOL8 centre; //Not at word end chs + BOOL8 good_quality_word; + INT16 char_quality; + INT16 accepted_char_quality; + + INT16 conf_level; //0:REJECT + //1:DODGY ACCEPT + //2:DICT ACCEPT + //3:CLEAR ACCEPT + INT16 first_alphanum_idx; + + word_string = word->best_choice->string ().string (); + first_alphanum_idx = first_alphanum_pos (word_string); + word_in_dict = ((word->best_choice->permuter () == SYSTEM_DAWG_PERM) || + (word->best_choice->permuter () == FREQ_DAWG_PERM) || + (word->best_choice->permuter () == USER_DAWG_PERM)); + checked_dict_word = word_in_dict && (safe_dict_word (word_string) > 0); + sensible_word = acceptable_word_string (word_string) != AC_UNACCEPTABLE; + + word_char_quality(word, row, &char_quality, &accepted_char_quality); + good_quality_word = word->best_choice->string ().length () == char_quality; + + #ifndef SECURE_NAMES + if (nn_reject_debug) { + tprintf ("Dict: %c Checked Dict: %c Sensible: %c Quality: %c\n", + word_in_dict ? 'T' : 'F', + checked_dict_word ? 'T' : 'F', + sensible_word ? 'T' : 'F', good_quality_word ? 'T' : 'F'); + } + #endif + + if (word->best_choice->string ().length () != + word->outword->blob_list ()->length ()) { + #ifndef SECURE_NAMES + tprintf ("nn_match_word ASSERT FAIL String:\"%s\"; #Blobs=%d\n", + word->best_choice->string ().string (), + word->outword->blob_list ()->length ()); + #endif + err_exit(); + } + + copy_outword = *(word->outword); + copy_outword.baseline_denormalise (&word->denorm); + /* + For each character, generate and match a new image, containing JUST the + character we have clipped, centered in the image, on a white background. + Note that we MUST have a square image so that we can scale it uniformly in + x and y. We base the size on x_height as this can be found fairly reliably. + */ + net_image_size = (net_image_width > net_image_height) ? + net_image_width : net_image_height; + clip_image_size = (INT16) floor (0.5 + + net_image_size * word->x_height / + net_image_x_height); + if ((clip_image_size <= 1) || (net_image_size <= 1)) { + return; + } + + /* + Get the image of the word and the pix positions of each char + */ + char_clip_word(©_outword, page_image, pixrow_list, imlines, pix_box); +#ifndef GRAPHICS_DISABLED + if (show_char_clipping) { + win = display_clip_image (©_outword, page_image, + pixrow_list, pix_box); + } +#endif + pixrow_it.set_to_list (pixrow_list); + pixrow_it.move_to_first (); + for (pixrow_it.mark_cycle_pt (), i = 0; + !pixrow_it.cycled_list (); pixrow_it.forward (), i++) { + if (pixrow_it.data ()-> + bad_box (page_image.get_xsize (), page_image.get_ysize ())) + continue; + clip_image.create (clip_image_size, clip_image_size, 1); + //make bin imge + if (!copy_outword.flag (W_INVERSE)) + invert_image(&clip_image); //white background for black on white + pixrow_it.data ()->char_clip_image (imlines, pix_box, row, + clip_image, baseline_pos); + if (copy_outword.flag (W_INVERSE)) + invert_image(&clip_image); //invert white on black for scaling &NN + scaled_image.create (net_image_size, net_image_size, 1); + scale_image(clip_image, scaled_image); + baseline_pos *= net_image_size / clip_image_size; + //scale with im + centre = !pixrow_it.at_first () && !pixrow_it.at_last (); + + conf_level = nn_match_char (scaled_image, baseline_pos, + word_in_dict, checked_dict_word, + sensible_word, centre, + good_quality_word, word_string[i]); + if (word->reject_map[i].recoverable ()) { + if ((i == first_alphanum_idx) && + ((word_string[i] == 'I') || (word_string[i] == 'i'))) { + if (conf_level >= nn_conf_initial_i_level) + word->reject_map[i].setrej_nn_accept (); + //un-reject char + } + else if (conf_level > 0) + //un-reject char + word->reject_map[i].setrej_nn_accept (); + } +#ifndef GRAPHICS_DISABLED + if (show_char_clipping) + display_images(clip_image, scaled_image); +#endif + clip_image.destroy (); + scaled_image.destroy (); + } + + delete[]imlines; // Free array of imlines + delete pixrow_list; + +#ifndef GRAPHICS_DISABLED + if (show_char_clipping) { + destroy_window(win); + } +#endif +} + + +/************************************************************************* + * nn_match_char() + * Call Neural Net matcher to match a single character, given a scaled, + * square image + *************************************************************************/ + +INT16 nn_match_char( //of character + IMAGE &scaled_image, + float baseline_pos, //rel to scaled_image + BOOL8 dict_word, //part of dict wd? + BOOL8 checked_dict_word, //part of dict wd? + BOOL8 sensible_word, //part acceptable str? + BOOL8 centre, //not at word ends? + BOOL8 good_quality_word, //initial segmentation + char tess_ch //confirm this? + ) { + INT16 conf_level; //0..2 + INT32 row; + INT32 col; + INT32 y_size = scaled_image.get_ysize (); + INT32 start_y = y_size - (y_size - net_image_height) / 2 - 1; + INT32 end_y = start_y - net_image_height + 1; + IMAGELINE imline; + float *input_vector; + float *input_vec_ptr; + char top; + float top_score; + char next; + float next_score; + INT16 input_nodes = (net_image_height * net_image_width) + net_bl_nodes; + INT16 j; + + input_vector = (float *) alloc_mem (input_nodes * sizeof (float)); + input_vec_ptr = input_vector; + + invert_image(&scaled_image); //cos nns work better + for (row = start_y; row >= end_y; row--) { + scaled_image.fast_get_line (0, row, net_image_width, &imline); + for (col = 0; col < net_image_width; col++) + *input_vec_ptr++ = imline.pixels[col]; + } + /* + The bit map presented to the net may be shorter than the image, so shift + the coord to be relative to the bitmap portion. + */ + baseline_pos -= (y_size - net_image_height) / 2.0; + /* + Baseline pos is 0 if below bitmap, 1 if above and in proportion otherwise. + This is represented to the net as a set of bl_nodes, an initial proportion + of which are set to 1.0, indicating the level of the baseline. The + remainder are 0.0 + */ + + if (baseline_pos < 0) + baseline_pos = 0; + else if (baseline_pos >= net_image_height) + baseline_pos = net_image_height + 1; + else + baseline_pos = baseline_pos + 1; + baseline_pos = baseline_pos / (net_image_height + 1); + + if (net_bl_nodes > 0) { + baseline_pos *= 1.7; //Use a wider range + if (net_bl_nodes > 1) { + /* Multi-node baseline representation */ + for (j = 0; j < net_bl_nodes; j++) { + if (baseline_pos > ((float) j / net_bl_nodes)) + *input_vec_ptr++ = 1.0; + else + *input_vec_ptr++ = 0.0; + } + } + else { + /* Single node baseline */ + *input_vec_ptr++ = baseline_pos; + } + } + + callnet(input_vector, &top, &top_score, &next, &next_score); + conf_level = evaluate_net_match (top, top_score, next, next_score, + tess_ch, dict_word, checked_dict_word, + sensible_word, centre, good_quality_word); + #ifndef SECURE_NAMES + if (nn_reject_debug) { + tprintf ("top:\"%c\" %4.2f next:\"%c\" %4.2f TESS:\"%c\" Conf: %d\n", + top, top_score, next, next_score, tess_ch, conf_level); + } + #endif + free_mem(input_vector); + return conf_level; +} + + +INT16 evaluate_net_match(char top, + float top_score, + char next, + float next_score, + char tess_ch, + BOOL8 dict_word, + BOOL8 checked_dict_word, + BOOL8 sensible_word, + BOOL8 centre, + BOOL8 good_quality_word) { + INT16 accept_level; //0 Very clearly matched + //1 Clearly top + //2 Top but poor match + //3 Next & poor top match + //4 Next but good top match + //5 No chance + BOOL8 good_top_choice; + BOOL8 excellent_top_choice; + BOOL8 confusion_match = FALSE; + BOOL8 dodgy_char = !isalnum (tess_ch); + + good_top_choice = (top_score > nn_reject_threshold) && + (nn_reject_head_and_shoulders * top_score > next_score); + + excellent_top_choice = good_top_choice && + (top_score > nn_dodgy_char_threshold); + + if (top == tess_ch) { + if (excellent_top_choice) + accept_level = 0; + else if (good_top_choice) + accept_level = 1; //Top correct and well matched + else + accept_level = 2; //Top correct but poor match + } + else if ((nn_conf_1Il && + STRING (conflict_set_I_l_1).contains (tess_ch) && + STRING (conflict_set_I_l_1).contains (top)) || + (nn_conf_hyphen && + STRING (conflict_set_hyphen).contains (tess_ch) && + STRING (conflict_set_hyphen).contains (top)) || + (nn_conf_Ss && + STRING (conflict_set_S_s).contains (tess_ch) && + STRING (conflict_set_S_s).contains (top))) { + confusion_match = TRUE; + if (good_top_choice) + accept_level = 1; //Good top confusion + else + accept_level = 2; //Poor top confusion + } + else if ((nn_conf_1Il && + STRING (conflict_set_I_l_1).contains (tess_ch) && + STRING (conflict_set_I_l_1).contains (next)) || + (nn_conf_hyphen && + STRING (conflict_set_hyphen).contains (tess_ch) && + STRING (conflict_set_hyphen).contains (next)) || + (nn_conf_Ss && + STRING (conflict_set_S_s).contains (tess_ch) && + STRING (conflict_set_S_s).contains (next))) { + confusion_match = TRUE; + if (!good_top_choice) + accept_level = 3; //Next confusion and top match dodgy + else + accept_level = 4; //Next confusion and good top match + } + else if (next == tess_ch) { + if (!good_top_choice) + accept_level = 3; //Next match and top match dodgy + else + accept_level = 4; //Next match and good top match + } + else + accept_level = 5; + + /* Could allow some match flexibility here sS$ etc */ + + /* Now set confirmation level according to how much we can believe the tess + char. */ + + if ((accept_level == 0) && !confusion_match) + return 3; + + if ((accept_level <= 1) && + (!nn_conf_strict_on_dodgy_chs || !dodgy_char) && !confusion_match) + return 3; + + if ((accept_level == 2) && + !confusion_match && !dodgy_char && + good_quality_word && + dict_word && + (checked_dict_word || !nn_double_check_dict) && sensible_word) + return 2; + + if (confusion_match && + (accept_level <= nn_conf_accept_level) && + (good_quality_word || + (!nn_conf_test_good_qual && + !STRING (conflict_set_I_l_1).contains (tess_ch))) && + (dict_word || !nn_conf_test_dict) && + (checked_dict_word || !nn_conf_double_check_dict) && + (sensible_word || !nn_conf_test_sensible)) + return 1; + + if (!confusion_match && + nn_lax && + (accept_level == 3) && + (good_quality_word || !nn_conf_test_good_qual) && + (dict_word || !nn_conf_test_dict) && + (sensible_word || !nn_conf_test_sensible)) + return 1; + else + return 0; +} + + +/************************************************************************* + * dont_allow_dubious_chars() + * Let Rejects "eat" into adjacent "dubious" chars. I.e those prone to be wrong + * if adjacent to a reject. + *************************************************************************/ +void dont_allow_dubious_chars(WERD_RES *word) { + int i = 0; + int rej_pos; + int word_len = word->reject_map.length (); + + while (i < word_len) { + /* Find next reject */ + + while ((i < word_len) && (word->reject_map[i].accepted ())) + i++; + + if (i < word_len) { + rej_pos = i; + + /* Reject dubious chars to the left */ + i--; + while ((i >= 0) && + STRING (dubious_chars_left_of_reject).contains (word-> + best_choice-> + string () + [i])) { + word->reject_map[i--].setrej_dubious (); + } + + /* Skip adjacent rejects */ + + for (i = rej_pos; + (i < word_len) && (word->reject_map[i].rejected ()); i++); + + /* Reject dubious chars to the right */ + + while ((i < word_len) && + STRING (dubious_chars_right_of_reject).contains (word-> + best_choice-> + string () + [i])) { + word->reject_map[i++].setrej_dubious (); + } + } + } +} + + +/************************************************************************* + * dont_allow_1Il() + * Dont unreject LONE accepted 1Il conflict set chars + *************************************************************************/ +void dont_allow_1Il(WERD_RES *word) { + int i = 0; + int word_len = word->reject_map.length (); + const char *s = word->best_choice->string ().string (); + BOOL8 accepted_1Il = FALSE; + + for (i = 0; i < word_len; i++) { + if (word->reject_map[i].accepted ()) { + if (STRING (conflict_set_I_l_1).contains (s[i])) + accepted_1Il = TRUE; + else { + if (isalnum (s[i])) + return; // >=1 non 1Il ch accepted + } + } + } + if (!accepted_1Il) + return; //Nothing to worry about + + for (i = 0; i < word_len; i++) { + if (STRING (conflict_set_I_l_1).contains (s[i]) && + word->reject_map[i].accepted ()) + word->reject_map[i].setrej_postNN_1Il (); + } +} + + +INT16 count_alphanums( //how many alphanums + WERD_RES *word) { + int count = 0; + int i; + + for (i = 0; i < word->reject_map.length (); i++) { + if ((word->reject_map[i].accepted ()) && + (isalnum (word->best_choice->string ()[i]))) + count++; + } + return count; +} + + +void reject_mostly_rejects( //rej all if most rejectd + WERD_RES *word) { + /* Reject the whole of the word if the fraction of rejects exceeds a limit */ + + if ((float) word->reject_map.reject_count () / word->reject_map.length () >= + rej_whole_of_mostly_reject_word_fract) + word->reject_map.rej_word_mostly_rej (); +} + + +BOOL8 repeated_nonalphanum_wd(WERD_RES *word, ROW *row) { + INT16 char_quality; + INT16 accepted_char_quality; + + if (word->best_choice->string ().length () <= 1) + return FALSE; + + if (!STRING (ok_repeated_ch_non_alphanum_wds). + contains (word->best_choice->string ()[0])) + return FALSE; + + if (!repeated_ch_string (word->best_choice->string ().string ())) + return FALSE; + + word_char_quality(word, row, &char_quality, &accepted_char_quality); + + if ((word->best_choice->string ().length () == char_quality) && + (char_quality == accepted_char_quality)) + return TRUE; + else + return FALSE; +} + + +BOOL8 repeated_ch_string(const char *rep_ch_str) { + char c; + + if ((rep_ch_str == NULL) || (*rep_ch_str == '\0')) { + return FALSE; + } + + c = *rep_ch_str; + rep_ch_str++; + while (*rep_ch_str == c) { + rep_ch_str++; + } + if (*rep_ch_str == '\0') + return TRUE; + return FALSE; +} + + +INT16 safe_dict_word(const char *s) { + int dict_word_type; + + dict_word_type = dict_word (s); + if (dict_word_type == DOC_DAWG_PERM) + return 0; + else + return dict_word_type; +} + + +void flip_hyphens(WERD_RES *word) { + char *str = (char *) word->best_choice->string ().string (); + int i = 0; + PBLOB_IT outword_it; + int prev_right = -9999; + int next_left; + BOX out_box; + float aspect_ratio; + + if (tessedit_lower_flip_hyphen <= 1) + return; + + outword_it.set_to_list (word->outword->blob_list ()); + + for (outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); outword_it.forward (), i++) { + out_box = outword_it.data ()->bounding_box (); + if (outword_it.at_last ()) + next_left = 9999; + else + next_left = outword_it.data_relative (1)->bounding_box ().left (); + /* + Dont touch small or touching blobs - it is too dangerous + */ + if ((out_box.width () > 8 * word->denorm.scale ()) && + (out_box.left () > prev_right) && (out_box.right () < next_left)) { + aspect_ratio = out_box.width () / (float) out_box.height (); + if (str[i] == '.') { + if (aspect_ratio >= tessedit_upper_flip_hyphen) { + /* Certain HYPHEN */ + str[i] = '-'; + if (word->reject_map[i].rejected ()) + word->reject_map[i].setrej_hyphen_accept (); + } + if ((aspect_ratio > tessedit_lower_flip_hyphen) && + word->reject_map[i].accepted ()) + //Suspected HYPHEN + word->reject_map[i].setrej_hyphen (); + } + else if (str[i] == '-') { + if ((aspect_ratio >= tessedit_upper_flip_hyphen) && + (word->reject_map[i].rejected ())) + word->reject_map[i].setrej_hyphen_accept (); + //Certain HYPHEN + + if ((aspect_ratio <= tessedit_lower_flip_hyphen) && + (word->reject_map[i].accepted ())) + //Suspected HYPHEN + word->reject_map[i].setrej_hyphen (); + } + } + prev_right = out_box.right (); + } +} + + +void flip_0O(WERD_RES *word) { + char *str = (char *) word->best_choice->string ().string (); + int i; + PBLOB_IT outword_it; + BOX out_box; + + if (!tessedit_flip_0O) + return; + + outword_it.set_to_list (word->outword->blob_list ()); + + for (i = 0, outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); i++, outword_it.forward ()) { + if (isupper (str[i]) || isdigit (str[i])) { + out_box = outword_it.data ()->bounding_box (); + if ((out_box.top () < bln_baseline_offset + bln_x_height) || + (out_box.bottom () > bln_baseline_offset + bln_x_height / 4)) + return; //Beware words with sub/superscripts + } + } + + for (i = 1; str[i] != '\0'; i++, outword_it.forward ()) { + if ((str[i] == '0') || (str[i] == 'O')) { + /* A0A */ + if (non_O_upper (str[i - 1]) && non_O_upper (str[i + 1])) { + str[i] = 'O'; + } + /* A00A */ + if (non_O_upper (str[i - 1]) && + ((str[i + 1] == '0') || (str[i + 1] == 'O')) && + non_O_upper (str[i + 2])) { + str[i] = 'O'; + str[i + 1] = 'O'; + i++; + } + /* AA0 */ + if ((i > 1) && + non_O_upper (str[i - 2]) && + non_O_upper (str[i - 1]) && + !isdigit (str[i + 1]) && + (str[i + 1] != 'l') && (str[i + 1] != 'I')) { + str[i] = 'O'; + } + /* 9O9 */ + if (non_0_digit (str[i - 1]) && non_0_digit (str[i + 1])) { + str[i] = '0'; + } + /* 9OOO */ + if (non_0_digit (str[i - 1]) && + ((str[i + 1] == '0') || (str[i + 1] == 'O')) && + ((str[i + 2] == '0') || (str[i + 2] == 'O'))) { + str[i] = '0'; + str[i + 1] = '0'; + str[i + 2] = '0'; + i += 2; + } + /* 9OO */ + if (non_0_digit (str[i - 1]) && + ((str[i + 1] == '0') || (str[i + 1] == 'O')) && + !isupper (str[i + 2])) { + str[i] = '0'; + str[i + 1] = '0'; + i++; + } + /* 9O */ + if (non_0_digit (str[i - 1]) && !isupper (str[i + 1])) { + str[i] = '0'; + } + /* 9[.,]OOO.. */ + if ((i > 1) && + ((str[i - 1] == '.') || (str[i - 1] == ',')) && + (isdigit (str[i - 2]) || (str[i - 2] == 'O'))) { + if (str[i - 2] == 'O') + str[i - 2] = '0'; + while ((str[i] == 'O') || (str[i] == '0')) { + str[i++] = '0'; + } + i--; + } + } + } +} + + +BOOL8 non_O_upper(char c) { + return isupper (c) && (c != 'O'); +} + + +BOOL8 non_0_digit(char c) { + return isdigit (c) && (c != '0'); +} diff --git a/ccmain/reject.h b/ccmain/reject.h new file mode 100644 index 0000000000..6aeebde914 --- /dev/null +++ b/ccmain/reject.h @@ -0,0 +1,175 @@ +/********************************************************************** + * File: reject.h (Formerly reject.h) + * Description: Rejection functions used in tessedit + * Author: Phil Cheatle + * Created: Wed Sep 23 16:50:21 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef REJECT_H +#define REJECT_H + +#include "varable.h" +#include "pageres.h" +#include "notdll.h" + +extern INT_VAR_H (tessedit_reject_mode, 5, "Rejection algorithm"); +extern INT_VAR_H (tessedit_ok_mode, 5, "Acceptance decision algorithm"); +extern BOOL_VAR_H (tessedit_use_nn, TRUE, ""); +extern BOOL_VAR_H (tessedit_rejection_debug, FALSE, "Adaption debug"); +extern BOOL_VAR_H (tessedit_rejection_stats, FALSE, "Show NN stats"); +extern BOOL_VAR_H (tessedit_flip_0O, TRUE, "Contextual 0O O0 flips"); +extern double_VAR_H (tessedit_lower_flip_hyphen, 1.5, +"Aspect ratio dot/hyphen test"); +extern double_VAR_H (tessedit_upper_flip_hyphen, 1.8, +"Aspect ratio dot/hyphen test"); +extern BOOL_VAR_H (rej_trust_doc_dawg, FALSE, +"Use DOC dawg in 11l conf. detector"); +extern BOOL_VAR_H (rej_1Il_use_dict_word, FALSE, "Use dictword test"); +extern BOOL_VAR_H (rej_1Il_trust_permuter_type, TRUE, "Dont double check"); +extern BOOL_VAR_H (one_ell_conflict_default, TRUE, +"one_ell_conflict default"); +extern BOOL_VAR_H (show_char_clipping, FALSE, "Show clip image window?"); +extern BOOL_VAR_H (nn_debug, FALSE, "NN DEBUGGING?"); +extern BOOL_VAR_H (nn_reject_debug, FALSE, "NN DEBUG each char?"); +extern BOOL_VAR_H (nn_lax, FALSE, "Use 2nd rate matches"); +extern BOOL_VAR_H (nn_double_check_dict, FALSE, "Double check"); +extern BOOL_VAR_H (nn_conf_double_check_dict, TRUE, +"Double check for confusions"); +extern BOOL_VAR_H (nn_conf_1Il, TRUE, "NN use 1Il conflicts"); +extern BOOL_VAR_H (nn_conf_Ss, TRUE, "NN use Ss conflicts"); +extern BOOL_VAR_H (nn_conf_hyphen, TRUE, "NN hyphen conflicts"); +extern BOOL_VAR_H (nn_conf_test_good_qual, FALSE, "NN dodgy 1Il cross check"); +extern BOOL_VAR_H (nn_conf_test_dict, TRUE, "NN dodgy 1Il cross check"); +extern BOOL_VAR_H (nn_conf_test_sensible, TRUE, "NN dodgy 1Il cross check"); +extern BOOL_VAR_H (nn_conf_strict_on_dodgy_chs, TRUE, +"Require stronger NN match"); +extern double_VAR_H (nn_dodgy_char_threshold, 0.99, "min accept score"); +extern INT_VAR_H (nn_conf_accept_level, 4, "NN accept dodgy 1Il matches? "); +extern INT_VAR_H (nn_conf_initial_i_level, 3, +"NN accept initial Ii match level "); +extern BOOL_VAR_H (no_unrej_dubious_chars, TRUE, +"Dubious chars next to reject?"); +extern BOOL_VAR_H (no_unrej_no_alphanum_wds, TRUE, +"Stop unrej of non A/N wds?"); +extern BOOL_VAR_H (no_unrej_1Il, FALSE, "Stop unrej of 1Ilchars?"); +extern BOOL_VAR_H (rej_use_tess_accepted, TRUE, +"Individual rejection control"); +extern BOOL_VAR_H (rej_use_tess_blanks, TRUE, "Individual rejection control"); +extern BOOL_VAR_H (rej_use_good_perm, TRUE, "Individual rejection control"); +extern BOOL_VAR_H (rej_use_sensible_wd, FALSE, "Extend permuter check"); +extern BOOL_VAR_H (rej_alphas_in_number_perm, FALSE, "Extend permuter check"); +extern double_VAR_H (rej_whole_of_mostly_reject_word_fract, 0.85, +"if >this fract"); +extern INT_VAR_H (rej_mostly_reject_mode, 1, +"0-never, 1-afterNN, 2-after new xht"); +extern double_VAR_H (tessed_fullstop_aspect_ratio, 1.2, +"if >this fract then reject"); +extern INT_VAR_H (net_image_width, 40, "NN input image width"); +extern INT_VAR_H (net_image_height, 36, "NN input image height"); +extern INT_VAR_H (net_image_x_height, 22, "NN input image x_height"); +extern INT_VAR_H (tessedit_image_border, 2, "Rej blbs near image edge limit"); +extern INT_VAR_H (net_bl_nodes, 20, "Number of baseline nodes"); +extern double_VAR_H (nn_reject_threshold, 0.5, "NN min accept score"); +extern double_VAR_H (nn_reject_head_and_shoulders, 0.6, +"top scores sep factor"); +extern STRING_VAR_H (ok_single_ch_non_alphanum_wds, "-?\075", +"Allow NN to unrej"); +extern STRING_VAR_H (ok_repeated_ch_non_alphanum_wds, "-?*\075", +"Allow NN to unrej"); +extern STRING_VAR_H (conflict_set_I_l_1, "Il1[]", "Il1 conflict set"); +extern STRING_VAR_H (conflict_set_S_s, "Ss$", "Ss conflict set"); +extern STRING_VAR_H (conflict_set_hyphen, "-_~", "hyphen conflict set"); +extern STRING_VAR_H (dubious_chars_left_of_reject, "!'+`()-./\\<>;:^_,~\"", +"Unreliable chars"); +extern STRING_VAR_H (dubious_chars_right_of_reject, "!'+`()-./\\<>;:^_,~\"", +"Unreliable chars"); +extern INT_VAR_H (min_sane_x_ht_pixels, 8, +"Reject any x-ht lt or eq than this"); +void set_done( //set done flag + WERD_RES *word, + INT16 pass); +void make_reject_map( //make rej map for wd //detailed results + WERD_RES *word, + BLOB_CHOICE_LIST_CLIST *blob_choices, + ROW *row, + INT16 pass //1st or 2nd? + ); +void reject_blanks(WERD_RES *word); +void reject_I_1_L(WERD_RES *word); + //detailed results +void reject_poor_matches(WERD_RES *word, BLOB_CHOICE_LIST_CLIST *blob_choices); +float compute_reject_threshold( //compute threshold //detailed results + BLOB_CHOICE_LIST_CLIST *blob_choices); +int sort_floats( //qsort function + const void *arg1, //ptrs to floats + const void *arg2); +void reject_edge_blobs(WERD_RES *word); +BOOL8 one_ell_conflict(WERD_RES *word_res, BOOL8 update_map); +INT16 first_alphanum_pos(const char *word); +INT16 alpha_count(const char *word); +BOOL8 word_contains_non_1_digit(const char *word); +BOOL8 test_ambig_word( //test for ambiguity + WERD_RES *word); + //original word +BOOL8 ambig_word(const char *start_word, + char *temp_word, //alterable copy + INT16 test_char_pos //idx to char to alter + ); +const char *char_ambiguities(char c); + +#ifndef EMBEDDED +void test_ambigs(const char *word); +#endif + +void nn_recover_rejects(WERD_RES *word, ROW *row); +void nn_match_word( //Match a word + WERD_RES *word, + ROW *row); + //of character +INT16 nn_match_char(IMAGE &scaled_image, + float baseline_pos, //rel to scaled_image + BOOL8 dict_word, //part of dict wd? + BOOL8 checked_dict_word, //part of dict wd? + BOOL8 sensible_word, //part acceptable str? + BOOL8 centre, //not at word ends? + BOOL8 good_quality_word, //initial segmentation + char tess_ch //confirm this? + ); +INT16 evaluate_net_match(char top, + float top_score, + char next, + float next_score, + char tess_ch, + BOOL8 dict_word, + BOOL8 checked_dict_word, + BOOL8 sensible_word, + BOOL8 centre, + BOOL8 good_quality_word); +void dont_allow_dubious_chars(WERD_RES *word); + +void dont_allow_1Il(WERD_RES *word); + +INT16 count_alphanums( //how many alphanums + WERD_RES *word); +void reject_mostly_rejects( //rej all if most rejectd + WERD_RES *word); +BOOL8 repeated_nonalphanum_wd(WERD_RES *word, ROW *row); +BOOL8 repeated_ch_string(const char *rep_ch_str); +INT16 safe_dict_word(const char *s); +void flip_hyphens(WERD_RES *word); +void flip_0O(WERD_RES *word); +BOOL8 non_O_upper(char c); +BOOL8 non_0_digit(char c); +#endif diff --git a/ccmain/scaleimg.cpp b/ccmain/scaleimg.cpp new file mode 100644 index 0000000000..7f02d2759e --- /dev/null +++ b/ccmain/scaleimg.cpp @@ -0,0 +1,366 @@ +/********************************************************************** + * File: scaleimg.cpp (Formerly scaleim.c) + * Description: Smart scaling of images. + * Author: Phil Cheatle + * Created: Wed Nov 18 16:12:03 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +/************************************************************************* + * This is really Sheelagh's code that I've hacked into a more usable form. + * You simply call scale_image() passing in source and target images. The target + * image should be empty, but created - in order to define the destination + * size. + *************************************************************************/ + +#include "mfcpch.h" +#include +#include +#include "fileerr.h" +#include "tprintf.h" +#include "grphics.h" +#include "img.h" +//#include "basefile.h" +#include "imgscale.h" +#include "scaleimg.h" + +void scale_image( //scale an image + IMAGE &image, //source image + IMAGE &target_image //target image + ) { + INT32 xsize, ysize, new_xsize, new_ysize; + IMAGELINE line, new_line; + int *hires, *lores, *oldhires, *oldlores; + int i, j, n, oldn, row, col; + int offset = 0; //not used here + float factor; + UINT8 curr_colour, new_colour; + int dummy = -1; + IMAGE image2; //horiz scaled image + + xsize = image.get_xsize (); + ysize = image.get_ysize (); + new_xsize = target_image.get_xsize (); + new_ysize = target_image.get_ysize (); + if (new_ysize > new_xsize) + new_line.init (new_ysize); + else + new_line.init (new_xsize); + + factor = (float) xsize / (float) new_xsize; + + hires = (int *) calloc (xsize, sizeof (int)); + lores = (int *) calloc (new_xsize, sizeof (int)); + oldhires = (int *) calloc (xsize, sizeof (int)); + oldlores = (int *) calloc (new_xsize, sizeof (int)); + if ((hires == NULL) || (lores == NULL) || (oldhires == NULL) + || (oldlores == NULL)) { + fprintf (stderr, "Calloc error in scale_image\n"); + err_exit(); + } + + image2.create (new_xsize, ysize, image.get_bpp ()); + + oldn = 0; + /* do first row separately because hires[col-1] doesn't make sense here */ + image.fast_get_line (0, 0, xsize, &line); + /* each line nominally begins with white */ + curr_colour = 1; + n = 0; + for (i = 0; i < xsize; i++) { + new_colour = *(line.pixels + i); + if (new_colour != curr_colour) { + hires[n] = i; + n++; + curr_colour = new_colour; + } + } + if (offset != 0) + for (i = 0; i < n; i++) + hires[i] += offset; + + if (n > new_xsize) { + tprintf ("Too many transitions (%d) on line 0\n", n); + scale_image_cop_out(image, + target_image, + factor, + hires, + lores, + oldhires, + oldlores); + return; + } + else if (n > 0) + dyn_prog (n, hires, lores, new_xsize, &dummy, &dummy, 0, factor); + else + lores[0] = new_xsize; + + curr_colour = 1; + j = 0; + for (i = 0; i < new_xsize; i++) { + if (lores[j] == i) { + curr_colour = 1 - curr_colour; + j++; + } + *(new_line.pixels + i) = curr_colour; + } + image2.put_line (0, 0, new_xsize, &new_line, 0); + + for (i = 0; i < n; i++) { + oldhires[i] = hires[i]; + oldlores[i] = lores[i]; + } + + for (i = n; i < oldn; i++) { + oldhires[i] = 0; + oldlores[i] = 0; + } + oldn = n; + + for (row = 1; row < ysize; row++) { + image.fast_get_line (0, row, xsize, &line); + /* each line nominally begins with white */ + curr_colour = 1; + n = 0; + for (i = 0; i < xsize; i++) { + new_colour = *(line.pixels + i); + if (new_colour != curr_colour) { + hires[n] = i; + n++; + curr_colour = new_colour; + } + } + for (i = n; i < oldn; i++) { + hires[i] = 0; + lores[i] = 0; + } + if (offset != 0) + for (i = 0; i < n; i++) + hires[i] += offset; + + if (n > new_xsize) { + tprintf ("Too many transitions (%d) on line %d\n", n, row); + scale_image_cop_out(image, + target_image, + factor, + hires, + lores, + oldhires, + oldlores); + return; + } + else if (n > 0) + dyn_prog(n, hires, lores, new_xsize, oldhires, oldlores, oldn, factor); + else + lores[0] = new_xsize; + + curr_colour = 1; + j = 0; + for (i = 0; i < new_xsize; i++) { + if (lores[j] == i) { + curr_colour = 1 - curr_colour; + j++; + } + *(new_line.pixels + i) = curr_colour; + } + image2.put_line (0, row, new_xsize, &new_line, 0); + + for (i = 0; i < n; i++) { + oldhires[i] = hires[i]; + oldlores[i] = lores[i]; + } + for (i = n; i < oldn; i++) { + oldhires[i] = 0; + oldlores[i] = 0; + } + oldn = n; + } + + free(hires); + free(lores); + free(oldhires); + free(oldlores); + + /* NOW DO THE VERTICAL SCALING from image2 to target_image*/ + + xsize = new_xsize; + factor = (float) ysize / (float) new_ysize; + offset = 0; + + hires = (int *) calloc (ysize, sizeof (int)); + lores = (int *) calloc (new_ysize, sizeof (int)); + oldhires = (int *) calloc (ysize, sizeof (int)); + oldlores = (int *) calloc (new_ysize, sizeof (int)); + if ((hires == NULL) || (lores == NULL) || (oldhires == NULL) + || (oldlores == NULL)) { + fprintf (stderr, "Calloc error in scale_image (vert)\n"); + err_exit(); + } + + oldn = 0; + /* do first col separately because hires[col-1] doesn't make sense here */ + image2.get_column (0, 0, ysize, &line, 0); + /* each line nominally begins with white */ + curr_colour = 1; + n = 0; + for (i = 0; i < ysize; i++) { + new_colour = *(line.pixels + i); + if (new_colour != curr_colour) { + hires[n] = i; + n++; + curr_colour = new_colour; + } + } + + if (offset != 0) + for (i = 0; i < n; i++) + hires[i] += offset; + + if (n > new_ysize) { + tprintf ("Too many transitions (%d) on column 0\n", n); + scale_image_cop_out(image, + target_image, + factor, + hires, + lores, + oldhires, + oldlores); + return; + } + else if (n > 0) + dyn_prog (n, hires, lores, new_ysize, &dummy, &dummy, 0, factor); + else + lores[0] = new_ysize; + + curr_colour = 1; + j = 0; + for (i = 0; i < new_ysize; i++) { + if (lores[j] == i) { + curr_colour = 1 - curr_colour; + j++; + } + *(new_line.pixels + i) = curr_colour; + } + target_image.put_column (0, 0, new_ysize, &new_line, 0); + + for (i = 0; i < n; i++) { + oldhires[i] = hires[i]; + oldlores[i] = lores[i]; + } + for (i = n; i < oldn; i++) { + oldhires[i] = 0; + oldlores[i] = 0; + } + oldn = n; + + for (col = 1; col < xsize; col++) { + image2.get_column (col, 0, ysize, &line, 0); + /* each line nominally begins with white */ + curr_colour = 1; + n = 0; + for (i = 0; i < ysize; i++) { + new_colour = *(line.pixels + i); + if (new_colour != curr_colour) { + hires[n] = i; + n++; + curr_colour = new_colour; + } + } + for (i = n; i < oldn; i++) { + hires[i] = 0; + lores[i] = 0; + } + + if (offset != 0) + for (i = 0; i < n; i++) + hires[i] += offset; + + if (n > new_ysize) { + tprintf ("Too many transitions (%d) on column %d\n", n, col); + scale_image_cop_out(image, + target_image, + factor, + hires, + lores, + oldhires, + oldlores); + return; + } + else if (n > 0) + dyn_prog(n, hires, lores, new_ysize, oldhires, oldlores, oldn, factor); + else + lores[0] = new_ysize; + + curr_colour = 1; + j = 0; + for (i = 0; i < new_ysize; i++) { + if (lores[j] == i) { + curr_colour = 1 - curr_colour; + j++; + } + *(new_line.pixels + i) = curr_colour; + } + target_image.put_column (col, 0, new_ysize, &new_line, 0); + + for (i = 0; i < n; i++) { + oldhires[i] = hires[i]; + oldlores[i] = lores[i]; + } + for (i = n; i < oldn; i++) { + oldhires[i] = 0; + oldlores[i] = 0; + } + oldn = n; + } + free(hires); + free(lores); + free(oldhires); + free(oldlores); +} + + +/********************************************************************** + * scale_image_cop_out + * + * Cop-out of scale_image by doing it the easy way and free the data. + **********************************************************************/ + +void scale_image_cop_out( //scale an image + IMAGE &image, //source image + IMAGE &target_image, //target image + float factor, //scale factor + int *hires, + int *lores, + int *oldhires, + int *oldlores) { + INT32 xsize, ysize, new_xsize, new_ysize; + + xsize = image.get_xsize (); + ysize = image.get_ysize (); + new_xsize = target_image.get_xsize (); + new_ysize = target_image.get_ysize (); + + if (factor <= 0.5) + reduce_sub_image (&image, 0, 0, xsize, ysize, + &target_image, 0, 0, (INT32) (1.0 / factor), FALSE); + else if (factor >= 2) + enlarge_sub_image (&image, 0, 0, &target_image, + 0, 0, new_xsize, new_ysize, (INT32) factor, FALSE); + else + copy_sub_image (&image, 0, 0, xsize, ysize, &target_image, 0, 0, FALSE); + free(hires); + free(lores); + free(oldhires); + free(oldlores); +} diff --git a/ccmain/scaleimg.h b/ccmain/scaleimg.h new file mode 100644 index 0000000000..4807b09e2c --- /dev/null +++ b/ccmain/scaleimg.h @@ -0,0 +1,35 @@ +/********************************************************************** + * File: scaleimg.h (Formerly scaleim.h) + * Description: Smart scaling of images. + * Author: Phil Cheatle + * Created: Wed Nov 18 16:12:03 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef SCALEIMG_H +#define SCALEIMG_H + +void scale_image( //scale an image + IMAGE &image, //source image + IMAGE &target_image //target image + ); +void scale_image_cop_out( //scale an image + IMAGE &image, //source image + IMAGE &target_image, //target image + float factor, //scale factor + int *hires, + int *lores, + int *oldhires, + int *oldlores); +#endif diff --git a/ccmain/tessbox.cpp b/ccmain/tessbox.cpp new file mode 100644 index 0000000000..99ae8da0c2 --- /dev/null +++ b/ccmain/tessbox.cpp @@ -0,0 +1,370 @@ +/********************************************************************** + * File: tessbox.cpp (Formerly tessbox.c) + * Description: Black boxed Tess for developing a resaljet. + * Author: Ray Smith + * Created: Thu Apr 23 11:03:36 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "tfacep.h" +#include "tfacepp.h" +#include "tessbox.h" +#include "mfoutline.h" + +#define EXTERN + +/********************************************************************** + * tess_segment_pass1 + * + * Segment a word using the pass1 conditions of the tess segmenter. + **********************************************************************/ + +WERD_CHOICE *tess_segment_pass1( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + WERD_CHOICE *result; //return value + int saved_enable_assoc = 0; + int saved_chop_enable = 0; + + if (word->flag (W_DONT_CHOP)) { + saved_enable_assoc = enable_assoc; + saved_chop_enable = chop_enable; + enable_assoc = 0; + chop_enable = 0; + if (word->flag (W_REP_CHAR)) + permute_only_top = 1; + } + set_pass1(); + // tprintf("pass1 chop on=%d, seg=%d, onlytop=%d",chop_enable,enable_assoc,permute_only_top); + result = recog_word (word, denorm, matcher, NULL, NULL, FALSE, + raw_choice, blob_choices, outword); + if (word->flag (W_DONT_CHOP)) { + enable_assoc = saved_enable_assoc; + chop_enable = saved_chop_enable; + permute_only_top = 0; + } + return result; +} + + +/********************************************************************** + * tess_segment_pass2 + * + * Segment a word using the pass2 conditions of the tess segmenter. + **********************************************************************/ + +WERD_CHOICE *tess_segment_pass2( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + WERD_CHOICE *result; //return value + int saved_enable_assoc = 0; + int saved_chop_enable = 0; + + if (word->flag (W_DONT_CHOP)) { + saved_enable_assoc = enable_assoc; + saved_chop_enable = chop_enable; + enable_assoc = 0; + chop_enable = 0; + if (word->flag (W_REP_CHAR)) + permute_only_top = 1; + } + set_pass2(); + result = recog_word (word, denorm, matcher, NULL, NULL, FALSE, + raw_choice, blob_choices, outword); + if (word->flag (W_DONT_CHOP)) { + enable_assoc = saved_enable_assoc; + chop_enable = saved_chop_enable; + permute_only_top = 0; + } + return result; +} + + +/********************************************************************** + * correct_segment_pass2 + * + * Segment a word correctly using the pass2 conditions of the tess segmenter. + * Then call the tester with all the correctly segmented blobs. + * If the correct segmentation cannot be found, the tester is called + * with the segmentation found by tess and all the correct flags set to + * false and all strings are NULL. + **********************************************************************/ + +WERD_CHOICE *correct_segment_pass2( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + set_pass2(); + return recog_word (word, denorm, matcher, NULL, tester, TRUE, + raw_choice, blob_choices, outword); +} + + +/********************************************************************** + * test_segment_pass2 + * + * Segment a word correctly using the pass2 conditions of the tess segmenter. + * Then call the tester on all words used by tess in its search. + * Do this only on words where the correct segmentation could be found. + **********************************************************************/ + +WERD_CHOICE *test_segment_pass2( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + set_pass2(); + return recog_word (word, denorm, matcher, tester, NULL, TRUE, + raw_choice, blob_choices, outword); +} + + +/********************************************************************** + * tess_acceptable_word + * + * Return true if the word is regarded as "good enough". + **********************************************************************/ + +BOOL8 tess_acceptable_word( //test acceptability + WERD_CHOICE *word_choice, //after context + WERD_CHOICE *raw_choice //before context + ) { + A_CHOICE choice; //after context + A_CHOICE tess_raw; //before + + choice.rating = word_choice->rating (); + choice.certainty = word_choice->certainty (); + choice.string = (char *) word_choice->string ().string (); + tess_raw.rating = raw_choice->rating (); + tess_raw.certainty = raw_choice->certainty (); + tess_raw.string = (char *) raw_choice->string ().string (); + //call tess + return AcceptableResult (&choice, &tess_raw); +} + + +/********************************************************************** + * tess_adaptable_word + * + * Return true if the word is regarded as "good enough". + **********************************************************************/ + +BOOL8 tess_adaptable_word( //test adaptability + WERD *word, //word to test + WERD_CHOICE *word_choice, //after context + WERD_CHOICE *raw_choice //before context + ) { + TWERD *tessword; //converted word + INT32 result; //answer + + tessword = make_tess_word (word, NULL); + result = AdaptableWord (tessword, word_choice->string ().string (), + raw_choice->string ().string ()); + delete_word(tessword); + return result != 0; +} + + +/********************************************************************** + * tess_cn_matcher + * + * Match a blob using the Tess Char Normalized (non-adaptive) matcher + * only. + **********************************************************************/ + +void tess_cn_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ) { + LIST result; //tess output + TBLOB *tessblob; //converted blob + TEXTROW tessrow; //dummy row + + tess_cn_matching = TRUE; //turn it on + tess_bn_matching = FALSE; + //convert blob + tessblob = make_tess_blob (blob, TRUE); + //make dummy row + make_tess_row(denorm, &tessrow); + //classify + result = AdaptiveClassifier (tessblob, NULL, &tessrow); + free_blob(tessblob); + //make our format + convert_choice_list(result, ratings); +} + + +/********************************************************************** + * tess_bn_matcher + * + * Match a blob using the Tess Baseline Normalized (adaptive) matcher + * only. + **********************************************************************/ + +void tess_bn_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ) { + LIST result; //tess output + TBLOB *tessblob; //converted blob + TEXTROW tessrow; //dummy row + + tess_bn_matching = TRUE; //turn it on + tess_cn_matching = FALSE; + //convert blob + tessblob = make_tess_blob (blob, TRUE); + //make dummy row + make_tess_row(denorm, &tessrow); + //classify + result = AdaptiveClassifier (tessblob, NULL, &tessrow); + free_blob(tessblob); + //make our format + convert_choice_list(result, ratings); +} + + +/********************************************************************** + * tess_default_matcher + * + * Match a blob using the default functionality of the Tess matcher. + **********************************************************************/ + +void tess_default_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ) { + LIST result; //tess output + TBLOB *tessblob; //converted blob + TEXTROW tessrow; //dummy row + + tess_bn_matching = FALSE; //turn it off + tess_cn_matching = FALSE; + //convert blob + tessblob = make_tess_blob (blob, TRUE); + //make dummy row + make_tess_row(denorm, &tessrow); + //classify + result = AdaptiveClassifier (tessblob, NULL, &tessrow); + free_blob(tessblob); + //make our format + convert_choice_list(result, ratings); +} + + +/********************************************************************** + * tess_training_tester + * + * Matcher tester function which actually trains tess. + **********************************************************************/ + +void tess_training_tester( //call tess + PBLOB *blob, //blob to match + DENORM *denorm, //de-normaliser + BOOL8 correct, //ly segmented + char *text, //correct text + INT32 count, //chars in text + BLOB_CHOICE_LIST *ratings //list of results + ) { + TBLOB *tessblob; //converted blob + TEXTROW tessrow; //dummy row + + if (correct) { + NormMethod = character; //Force char norm spc 30/11/93 + tess_bn_matching = FALSE; //turn it off + tess_cn_matching = FALSE; + //convert blob + tessblob = make_tess_blob (blob, TRUE); + //make dummy row + make_tess_row(denorm, &tessrow); + //learn it + LearnBlob(tessblob, &tessrow, text, count); + free_blob(tessblob); + } +} + + +/********************************************************************** + * tess_adapter + * + * Adapt to the word using the Tesseract mechanism. + **********************************************************************/ + +void tess_adapter( //adapt to word + WERD *word, //bln word + DENORM *denorm, //de-normalise + const char *string, //string for word + const char *raw_string, //before context + const char *rejmap //reject map + ) { + TWERD *tessword; //converted word + static TEXTROW tessrow; //dummy row + + //make dummy row + make_tess_row(denorm, &tessrow); + //make a word + tessword = make_tess_word (word, &tessrow); + AdaptToWord(tessword, &tessrow, string, raw_string, rejmap); + //adapt to it + delete_word(tessword); //free it +} + + +/********************************************************************** + * tess_add_doc_word + * + * Add the given word to the document dictionary + **********************************************************************/ + +void tess_add_doc_word( //test acceptability + WERD_CHOICE *word_choice //after context + ) { + A_CHOICE choice; //after context + + choice.rating = word_choice->rating (); + choice.certainty = word_choice->certainty (); + choice.string = (char *) word_choice->string ().string (); + add_document_word(&choice); +} diff --git a/ccmain/tessbox.h b/ccmain/tessbox.h new file mode 100644 index 0000000000..fd8dd45271 --- /dev/null +++ b/ccmain/tessbox.h @@ -0,0 +1,110 @@ +/********************************************************************** + * File: tessbox.h (Formerly tessbox.h) + * Description: Black boxed Tess for developing a resaljet. + * Author: Ray Smith + * Created: Thu Apr 23 11:03:36 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSBOX_H +#define TESSBOX_H + +#include "ratngs.h" +#include "notdll.h" + +WERD_CHOICE *tess_segment_pass1( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); +WERD_CHOICE *tess_segment_pass2( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); + //recog one word +WERD_CHOICE *correct_segment_pass2(WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); +WERD_CHOICE *test_segment_pass2( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); +BOOL8 tess_acceptable_word( //test acceptability + WERD_CHOICE *word_choice, //after context + WERD_CHOICE *raw_choice //before context + ); +BOOL8 tess_adaptable_word( //test adaptability + WERD *word, //word to test + WERD_CHOICE *word_choice, //after context + WERD_CHOICE *raw_choice //before context + ); +void tess_cn_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ); +void tess_bn_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ); +void tess_default_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ); +void tess_training_tester( //call tess + PBLOB *blob, //blob to match + DENORM *denorm, //de-normaliser + BOOL8 correct, //ly segmented + char *text, //correct text + INT32 count, //chars in text + BLOB_CHOICE_LIST *ratings //list of results + ); +void tess_adapter( //adapt to word + WERD *word, //bln word + DENORM *denorm, //de-normalise + const char *string, //string for word + const char *raw_string, //before context + const char *rejmap); +void tess_add_doc_word( //test acceptability + WERD_CHOICE *word_choice //after context + ); +#endif diff --git a/ccmain/tessedit.cpp b/ccmain/tessedit.cpp new file mode 100644 index 0000000000..4d6f2d0622 --- /dev/null +++ b/ccmain/tessedit.cpp @@ -0,0 +1,321 @@ +/********************************************************************** + * File: tessedit.cpp (Formerly tessedit.c) + * Description: Main program for merge of tess and editor. + * Author: Ray Smith + * Created: Tue Jan 07 15:21:46 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +//#include +//#include +//#include +//#include +#include "tfacep.h" //must be before main.h +//#include "fileerr.h" +#include "stderr.h" +#include "basedir.h" +#include "tessvars.h" +//#include "debgwin.h" +//#include "epapdest.h" +#include "control.h" +#include "imgs.h" +#include "reject.h" +#include "pageres.h" +//#include "gpapdest.h" +#include "mainblk.h" +#include "nwmain.h" +#include "pgedit.h" +#include "ocrshell.h" +#include "tprintf.h" +//#include "ipeerr.h" +//#include "restart.h" +#include "tessedit.h" +//#include "fontfind.h" +#include "permute.h" +#include "permdawg.h" +#include "permnum.h" +#include "stopper.h" +#include "adaptmatch.h" +#include "intmatcher.h" +#include "chop.h" +#include "globals.h" + +//extern "C" { +#include "callnet.h" //phils nn stuff +//} +#include "notdll.h" //phils nn stuff + +#define VARDIR "configs/" /*variables files */ + //config under api +#define API_CONFIG "configs/api_config" +#define EXTERN + +EXTERN BOOL_EVAR (tessedit_write_vars, FALSE, "Write all vars to file"); +EXTERN BOOL_VAR (tessedit_tweaking_tess_vars, FALSE, +"Fiddle tess config values"); + +EXTERN INT_VAR (tweak_ReliableConfigThreshold, 2, "Tess VAR"); + +EXTERN double_VAR (tweak_garbage, 1.5, "Tess VAR"); +EXTERN double_VAR (tweak_ok_word, 1.25, "Tess VAR"); +EXTERN double_VAR (tweak_good_word, 1.1, "Tess VAR"); +EXTERN double_VAR (tweak_freq_word, 1.0, "Tess VAR"); +EXTERN double_VAR (tweak_ok_number, 1.4, "Tess VAR"); +EXTERN double_VAR (tweak_good_number, 1.1, "Tess VAR"); +EXTERN double_VAR (tweak_non_word, 1.25, "Tess VAR"); +EXTERN double_VAR (tweak_CertaintyPerChar, -0.5, "Tess VAR"); +EXTERN double_VAR (tweak_NonDictCertainty, -2.5, "Tess VAR"); +EXTERN double_VAR (tweak_RejectCertaintyOffset, 1.0, "Tess VAR"); +EXTERN double_VAR (tweak_GoodAdaptiveMatch, 0.125, "Tess VAR"); +EXTERN double_VAR (tweak_GreatAdaptiveMatch, 0.10, "Tess VAR"); +EXTERN INT_VAR (tweak_AdaptProtoThresh, 230, "Tess VAR"); +EXTERN INT_VAR (tweak_AdaptFeatureThresh, 230, "Tess VAR"); +EXTERN INT_VAR (tweak_min_outline_points, 6, "Tess VAR"); +EXTERN INT_VAR (tweak_min_outline_area, 2000, "Tess VAR"); +EXTERN double_VAR (tweak_good_split, 50.0, "Tess VAR"); +EXTERN double_VAR (tweak_ok_split, 100.0, "Tess VAR"); + +extern INT16 XOFFSET; +extern INT16 YOFFSET; +extern int NO_BLOCK; + + //progress monitor +ETEXT_DESC *global_monitor = NULL; + +int init_tesseract(const char *arg0, + const char *textbase, + const char *configfile, + int configc, + const char *const *configv) { + FILE *var_file; + static char c_path[MAX_PATH]; //path for c code + + // Set the basename, compute the data directory and read C++ configs. + main_setup(arg0, textbase, configc, configv); + debug_window_on.set_value (FALSE); + + if (tessedit_write_vars) { + var_file = fopen ("edited.cfg", "w"); + if (var_file != NULL) { + print_variables(var_file); + fclose(var_file); + } + } + strcpy (c_path, datadir.string ()); + c_path[strlen (c_path) - strlen (m_data_sub_dir.string ())] = '\0'; + demodir = c_path; + start_recog(configfile, textbase); + + ReliableConfigThreshold = tweak_ReliableConfigThreshold; + + set_tess_tweak_vars(); + + if (tessedit_use_nn) //phils nn stuff + init_net(); + return 0; //Normal exit +} + +void end_tesseract() { + end_recog(); +} + +#ifdef _TIFFIO_ +void read_tiff_image(TIFF* tif, IMAGE* image) { + tdata_t buf; + uint32 image_width, image_height; + uint16 photometric; + short bpp; + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &image_width); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &image_height); + TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bpp); + TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric); + // Tesseract's internal representation is 0-is-black, + // so if the photometric is 1 (min is black) then high-valued pixels + // are 1 (white), otherwise they are 0 (black). + UINT8 high_value = photometric == 1; + image->create(image_width, image_height, bpp); + IMAGELINE line; + line.init(image_width); + + buf = _TIFFmalloc(TIFFScanlineSize(tif)); + int bytes_per_line = (image_width*bpp + 7)/8; + UINT8* dest_buf = image->get_buffer(); + // This will go badly wrong with one of the more exotic tiff formats, + // but the majority will work OK. + for (int y = 0; y < image_height; ++y) { + TIFFReadScanline(tif, buf, y); + memcpy(dest_buf, buf, bytes_per_line); + dest_buf += bytes_per_line; + } + if (high_value == 0) + invert_image(image); + _TIFFfree(buf); +} +#endif + +/* Define command type identifiers */ + +enum CMD_EVENTS +{ + ACTION_1_CMD_EVENT, + RECOG_WERDS, + RECOG_PSEUDO, + ACTION_2_CMD_EVENT +}; + +/********************************************************************** + * extend_menu() + * + * Function called by pgeditor to let you extend the command menu. + * Items can be added to the "MODES" and "OTHER" menus. The modes_id_base + * and other_id_base parameters are required to offset your command event ids + * from those of pgeditor, and to let the pgeditor which commands are mode + * changes and which are unmoded commands. (Sorry if you think these offsets + * are a bit kludgy, the alternative would be to duplicate all the menu + * constructor modes within pgeditor so that the offsets could be hidden.) + * + * Items for the "MODES" menu may only be simple menu items (just a name and + * id). Items for the "OTHER" menu can be editable parameters or boolean + * toggles. Refer to menu.h to see how to build different types. + **********************************************************************/ + +void extend_menu( //handle for "MODES" + RADIO_MENU *modes_menu, + INT16 modes_id_base, //mode cmd ids offset + NON_RADIO_MENU *other_menu, //handle for "OTHER" + INT16 other_id_base //mode cmd ids offset + ) { + /* Example new mode */ + + modes_menu->add_child (new RADIO_MENU_LEAF ("Recog Words", + modes_id_base + RECOG_WERDS)); + modes_menu->add_child (new RADIO_MENU_LEAF ("Recog Blobs", + modes_id_base + RECOG_PSEUDO)); + + /* Example toggle + + other_menu->add_child( + new TOGGLE_MENU_LEAF( "Action 2", //Display string + other_id_base + ACTION_2_CMD_EVENT, //offset command id + FALSE ) ); //Initial value + + Example text parm (commented out) + + other_menu->add_child( + new VARIABLE_MENU_LEAF( "Parm change", //Display string + other_id_base + ACTION_3_CMD_EVENT, //offset command id + "default value" ) ); //default value string + */ +} + + +/********************************************************************** + * extend_moded_commands() + * + * Function called by pgeditor when the user is in one of the extended modes + * defined by extend_menu() and the user has selected an area in the image + * window. + **********************************************************************/ + +void extend_moded_commands( //current mode + INT32 mode, + BOX selection_box //area selected + ) { + char msg[MAX_CHARS + 1]; + + switch (mode) { + case RECOG_WERDS: + command_window->msg ("Recogging selected words"); + + /* This is how to apply a "word processor" function to each selected word */ + + process_selected_words(current_block_list, + selection_box, + &recog_interactive); + break; + case RECOG_PSEUDO: + command_window->msg ("Recogging selected blobs"); + + /* This is how to apply a "word processor" function to each selected word */ + + recog_pseudo_word(current_block_list, selection_box); + break; + default: + sprintf (msg, "Unexpected extended mode " INT32FORMAT, mode); + command_window->msg (msg); + } +} + + +/********************************************************************** + * extend_unmoded_commands() + * + * Function called by pgeditor when the user has selected one of the unmoded + * extended menu options. + **********************************************************************/ + +void extend_unmoded_commands( //current mode + INT32 cmd_event, + char *new_value //changed value if any + ) { + char msg[MAX_CHARS + 1]; + + switch (cmd_event) { + case ACTION_2_CMD_EVENT: //a toggle event + if (new_value[0] == 'T') + //Display message + command_window->msg ("Extended Action 2 ON!!"); + else + command_window->msg ("Extended Action 2 OFF!!"); + break; + default: + sprintf (msg, "Unrecognised extended command " INT32FORMAT " (%s)", + cmd_event, new_value); + command_window->msg (msg); + break; + } +} + + +/************************************************************************* + * set_tess_tweak_vars() + * Set TESS vars from the tweek value - This is only really of use during search + * of the space of tess configs - othertimes the default values are set + * + *************************************************************************/ +void set_tess_tweak_vars() { + if (tessedit_tweaking_tess_vars) { + garbage = tweak_garbage; + ok_word = tweak_ok_word; + good_word = tweak_good_word; + freq_word = tweak_freq_word; + ok_number = tweak_ok_number; + good_number = tweak_good_number; + non_word = tweak_non_word; + CertaintyPerChar = tweak_CertaintyPerChar; + NonDictCertainty = tweak_NonDictCertainty; + RejectCertaintyOffset = tweak_RejectCertaintyOffset; + GoodAdaptiveMatch = tweak_GoodAdaptiveMatch; + GreatAdaptiveMatch = tweak_GreatAdaptiveMatch; + AdaptProtoThresh = tweak_AdaptProtoThresh; + AdaptFeatureThresh = tweak_AdaptFeatureThresh; + min_outline_points = tweak_min_outline_points; + min_outline_area = tweak_min_outline_area; + good_split = tweak_good_split; + ok_split = tweak_ok_split; + } + // if (expiry_day * 24 * 60 * 60 < time(NULL)) + // err_exit(); +} diff --git a/ccmain/tessedit.h b/ccmain/tessedit.h new file mode 100644 index 0000000000..e1f6299ede --- /dev/null +++ b/ccmain/tessedit.h @@ -0,0 +1,67 @@ +/********************************************************************** + * File: tessedit.h (Formerly tessedit.h) + * Description: Main program for merge of tess and editor. + * Author: Ray Smith + * Created: Tue Jan 07 15:21:46 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSEDIT_H +#define TESSEDIT_H + +#include "tessclas.h" +#include "ocrclass.h" +#include "pgedit.h" +#include "notdll.h" + +// Includes libtiff if HAVE_LIBTIFF is defined +#ifdef HAVE_LIBTIFF +#ifdef GOOGLE3 +#include "third_party/tiff/tiffio.h" +#else +#include "tiffio.h" +#endif +#endif + + //progress monitor +extern ETEXT_DESC *global_monitor; + +int init_tesseract(const char *arg0, + const char *textbase, + const char *configfile, + int configc, + const char *const *configv); +void recognize_page(STRING& image_name); +void end_tesseract(); + +#ifdef _TIFFIO_ +void read_tiff_image(TIFF* tif, IMAGE* image); +#endif + +//handle for "MODES" +void extend_menu(RADIO_MENU *modes_menu, + INT16 modes_id_base, //mode cmd ids offset + NON_RADIO_MENU *other_menu, //handle for "OTHER" + INT16 other_id_base //mode cmd ids offset + ); + //current mode +void extend_moded_commands(INT32 mode, + BOX selection_box //area selected + ); + //current mode +void extend_unmoded_commands(INT32 cmd_event, + char *new_value //changed value if any + ); +void set_tess_tweak_vars(); +#endif diff --git a/ccmain/tessembedded.h b/ccmain/tessembedded.h new file mode 100644 index 0000000000..f3d44e689e --- /dev/null +++ b/ccmain/tessembedded.h @@ -0,0 +1,38 @@ +/********************************************************************** + * File: tessembedded.h + * Description: Access to initialization functions in embedded environment + * Author: Marius Renn + * Created: Sun Oct 21 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSEMBEDDED_H +#define TESSEMBEDDED_H + +#include "ocrblock.h" +#include "varable.h" +#include "notdll.h" + +int init_tessembedded(const char *arg0, + const char *textbase, + const char *configfile, + int configc, + const char *const *configv); + +void tessembedded_read_file(STRING &name, + BLOCK_LIST *blocks); + +void end_tessembedded(); + +#endif diff --git a/ccmain/tesseractmain.cpp b/ccmain/tesseractmain.cpp new file mode 100644 index 0000000000..8d8b22395c --- /dev/null +++ b/ccmain/tesseractmain.cpp @@ -0,0 +1,311 @@ +/********************************************************************** + * File: tessedit.cpp (Formerly tessedit.c) + * Description: Main program for merge of tess and editor. + * Author: Ray Smith + * Created: Tue Jan 07 15:21:46 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "applybox.h" +#include "control.h" +#include "tessvars.h" +#include "tessedit.h" +#include "baseapi.h" +#include "pageres.h" +#include "imgs.h" +#include "varabled.h" +#include "tprintf.h" +#include "tesseractmain.h" +#include "stderr.h" +#include "notdll.h" +#include "mainblk.h" +#include "globals.h" +#include "tfacep.h" +#include "callnet.h" + +#define VARDIR "configs/" /*variables files */ + //config under api +#define API_CONFIG "configs/api_config" +#define EXTERN + +EXTERN BOOL_VAR (tessedit_read_image, TRUE, "Ensure the image is read"); +EXTERN BOOL_VAR (tessedit_write_images, FALSE, +"Capture the image from the IPE"); +EXTERN BOOL_VAR (tessedit_debug_to_screen, FALSE, "Dont use debug file"); + +extern INT16 XOFFSET; +extern INT16 YOFFSET; +extern int NO_BLOCK; + +const ERRCODE USAGE = "Usage"; +char szAppName[] = "Tessedit"; //app name + +/********************************************************************** + * main() + * + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +int main(int argc, char **argv) { + STRING outfile; //output file + + if (argc < 3) { + USAGE.error (argv[0], EXIT, + "%s imagename outputbase [configfile [[+|-]varfile]...]\n", argv[0]); + } + + if (argc == 3) + TessBaseAPI::Init(argv[0], argv[1], NULL, false, 0, argv + 2); + else + TessBaseAPI::Init(argv[0], argv[1], argv[3], false, argc - 4, argv + 4); + + tprintf ("Tesseract Open Source OCR Engine\n"); + + IMAGE image; +#ifdef _TIFFIO_ + TIFF* tif = TIFFOpen(argv[1], "r"); + if (tif) { + read_tiff_image(tif, &image); + TIFFClose(tif); + } else { + READFAILED.error (argv[0], EXIT, argv[1]); + } +#else + if (image.read_header(argv[1]) < 0) + READFAILED.error (argv[0], EXIT, argv[1]); + if (image.read(image.get_ysize ()) < 0) { + MEMORY_OUT.error(argv[0], EXIT, "Read of image %s", + argv[1]); + } +#endif + int bytes_per_line = check_legal_image_size(image.get_xsize(), + image.get_ysize(), + image.get_bpp()); + char* text = TessBaseAPI::TesseractRect(image.get_buffer(), image.get_bpp()/8, + bytes_per_line, 0, 0, + image.get_xsize(), image.get_ysize()); + outfile = argv[2]; + outfile += ".txt"; + FILE* fp = fopen(outfile.string(), "w"); + if (fp != NULL) { + fwrite(text, 1, strlen(text), fp); + fclose(fp); + } + delete [] text; + TessBaseAPI::End(); + + return 0; //Normal exit +} +#else + +int main(int argc, char **argv) { + UINT16 lang; //language + STRING pagefile; //input file + + if (argc < 4) { + USAGE.error (argv[0], EXIT, + "%s imagename outputbase configfile [[+|-]varfile]...\n", argv[0]); + } + + time_t t_start = time(NULL); + + init_tessembedded (argv[0], argv[2], argv[3], argc - 4, argv + 4); + + tprintf ("Tesseract Open Source OCR Engine (graphics disabled)\n"); + + if (tessedit_read_image) { +#ifdef _TIFFIO_ + TIFF* tif = TIFFOpen(argv[1], "r"); + if (tif) { + read_tiff_image(tif); + TIFFClose(tif); + } else + READFAILED.error (argv[0], EXIT, argv[1]); + +#else + if (page_image.read_header (argv[1]) < 0) + READFAILED.error (argv[0], EXIT, argv[1]); + if (page_image.read (page_image.get_ysize ()) < 0) { + MEMORY_OUT.error (argv[0], EXIT, "Read of image %s", + argv[1]); + } +#endif + } + + pagefile = argv[1]; + + BLOCK_LIST current_block_list; + tessembedded_read_file(pagefile, ¤t_block_list); + tprintf ("Done reading files.\n"); + + PAGE_RES page_res(¤t_block_list); + + recog_all_words(&page_res, NULL); + + current_block_list.clear(); + ResetAdaptiveClassifier(); + + time_t t_end = time(NULL); + double secs = difftime(t_end, t_start); + tprintf ("Done. Number of seconds: %d\n", (int)secs); + return 0; //Normal exit +} + +#endif + +int initialized = 0; + +#ifdef __MSW32__ +/********************************************************************** + * WinMain + * + * Main function for a windows program. + **********************************************************************/ + +int WINAPI WinMain( //main for windows //command line + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpszCmdLine, + int nCmdShow) { + WNDCLASS wc; + HWND hwnd; + MSG msg; + + char **argv; + char *argsin[2]; + int argc; + int exit_code; + + wc.style = CS_NOCLOSE | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = NULL; //LoadIcon (NULL, IDI_APPLICATION); + wc.hCursor = NULL; //LoadCursor (NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); + wc.lpszMenuName = NULL; + wc.lpszClassName = szAppName; + + RegisterClass(&wc); + + hwnd = CreateWindow (szAppName, szAppName, + WS_OVERLAPPEDWINDOW | WS_DISABLED, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL); + + argsin[0] = strdup (szAppName); + argsin[1] = strdup (lpszCmdLine); + /*allocate memory for the args. There can never be more than half*/ + /*the total number of characters in the arguments.*/ + argv = + (char **) malloc (((strlen (argsin[0]) + strlen (argsin[1])) / 2 + 1) * + sizeof (char *)); + + /*now construct argv as it should be for C.*/ + argc = parse_args (2, argsin, argv); + + // ShowWindow (hwnd, nCmdShow); + // UpdateWindow (hwnd); + + if (initialized) { + exit_code = main (argc, argv); + free (argsin[0]); + free (argsin[1]); + free(argv); + return exit_code; + } + while (GetMessage (&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + if (initialized) { + exit_code = main (argc, argv); + break; + } + else + exit_code = msg.wParam; + } + free (argsin[0]); + free (argsin[1]); + free(argv); + return exit_code; +} + + +/********************************************************************** + * WndProc + * + * Function to respond to messages. + **********************************************************************/ + +LONG WINAPI WndProc( //message handler + HWND hwnd, //window with message + UINT msg, //message typ + WPARAM wParam, + LPARAM lParam) { + HDC hdc; + + if (msg == WM_CREATE) { + // + // Create a rendering context. + // + hdc = GetDC (hwnd); + ReleaseDC(hwnd, hdc); + initialized = 1; + return 0; + } + return DefWindowProc (hwnd, msg, wParam, lParam); +} + + +/********************************************************************** + * parse_args + * + * Turn a list of args into a new list of args with each separate + * whitespace spaced string being an arg. + **********************************************************************/ + +int +parse_args ( /*refine arg list */ +int argc, /*no of input args */ +char *argv[], /*input args */ +char *arglist[] /*output args */ +) { + int argcount; /*converted argc */ + char *testchar; /*char in option string */ + int arg; /*current argument */ + + argcount = 0; /*no of options */ + for (arg = 0; arg < argc; arg++) { + testchar = argv[arg]; /*start of arg */ + do { + while (*testchar + && (*testchar == ' ' || *testchar == '\n' + || *testchar == '\t')) + testchar++; /*skip white space */ + if (*testchar) { + /*new arg */ + arglist[argcount++] = testchar; + /*skip to white space */ + for (testchar++; *testchar && *testchar != ' ' && *testchar != '\n' && *testchar != '\t'; testchar++); + if (*testchar) + *testchar++ = '\0'; /*turn to separate args */ + } + } + while (*testchar); + } + return argcount; /*new number of args */ +} +#endif diff --git a/ccmain/tesseractmain.h b/ccmain/tesseractmain.h new file mode 100644 index 0000000000..0c2a9a19e6 --- /dev/null +++ b/ccmain/tesseractmain.h @@ -0,0 +1,58 @@ +/********************************************************************** + * File: tessedit.h (Formerly tessedit.h) + * Description: Main program for merge of tess and editor. + * Author: Ray Smith + * Created: Tue Jan 07 15:21:46 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSERACTMAIN_H +#define TESSERACTMAIN_H + +#include "varable.h" +#include "tessclas.h" +#include "notdll.h" +#include "tessembedded.h" + +extern BOOL_VAR_H (tessedit_read_image, TRUE, "Ensure the image is read"); +INT32 api_main( //run from api + const char *arg0, //program name + UINT16 lang //language + ); +INT16 setup_info( //setup dummy engine info + UINT16 lang, //user language + const char *name, //of engine + const char *version //of engine + ); +INT16 read_image( //read dummy image info + IMAGE *im_out //output image + ); +#ifdef __MSW32__ +int WINAPI WinMain( //main for windows //command line + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpszCmdLine, + int nCmdShow); +LONG WINAPI WndProc( //message handler + HWND hwnd, //window with message + UINT msg, //message typ + WPARAM wParam, + LPARAM lParam); +int parse_args ( /*refine arg list */ +int argc, /*no of input args */ +char *argv[], /*input args */ +char *arglist[] /*output args */ +); +#endif +#endif diff --git a/ccmain/tessvars.cpp b/ccmain/tessvars.cpp new file mode 100644 index 0000000000..1ec1c7b0a0 --- /dev/null +++ b/ccmain/tessvars.cpp @@ -0,0 +1,38 @@ +/********************************************************************** + * File: tessvars.cpp (Formerly tessvars.c) + * Description: Variables and other globals for tessedit. + * Author: Ray Smith + * Created: Mon Apr 13 13:13:23 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "tessvars.h" + +#define EXTERN + +EXTERN INT_VAR (tessedit_adapt_kludge, 0, +"Use acceptable result or dangambigs"); +EXTERN BOOL_VAR (interactive_mode, FALSE, "Run interactively?"); +EXTERN BOOL_VAR (edit_variables, FALSE, "Variables Editor Window?"); +// xiaofan EXTERN STRING_VAR(file_type,".bl","Filename extension"); +EXTERN STRING_VAR (file_type, ".tif", "Filename extension"); +INT_VAR (testedit_match_debug, 0, "Integer match debug ctrl"); +EXTERN INT_VAR (tessedit_dangambigs_chop, FALSE, +"Use DangAmbigs to direct chop"); +EXTERN INT_VAR (tessedit_dangambigs_assoc, FALSE, +"Use DangAmbigs to direct assoc"); + +EXTERN IMAGE page_image; //image of page +EXTERN FILE *debug_fp; //write debug stuff here diff --git a/ccmain/tessvars.h b/ccmain/tessvars.h new file mode 100644 index 0000000000..3e65fdbe89 --- /dev/null +++ b/ccmain/tessvars.h @@ -0,0 +1,48 @@ +/********************************************************************** + * File: tessvars.h (Formerly tessvars.h) + * Description: Variables and other globals for tessedit. + * Author: Ray Smith + * Created: Mon Apr 13 13:13:23 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSVARS_H +#define TESSVARS_H + +#include "varable.h" +#include "img.h" +#include "tordmain.h" +#include "notdll.h" + +extern INT_VAR_H (tessedit_adapt_kludge, 0, +"Use acceptable result or dangambigs"); +extern BOOL_VAR_H (interactive_mode, FALSE, "Run interactively?"); +extern BOOL_VAR_H (edit_variables, FALSE, "Variables Editor Window?"); +//xiaofan extern STRING_VAR_H(file_type,".bl","Filename extension"); +extern STRING_VAR_H (file_type, ".tif", "Filename extension"); +extern INT_VAR_H (tessedit_truncate_wordchoice_log, 10, +"Max words to keep in list"); +extern INT_VAR_H (testedit_match_debug, 0, "Integer match debug ctrl"); +extern INT_VAR_H (tessedit_truncate_chopper, 1, +"Shorten chopper seam search"); +extern INT_VAR_H (tessedit_fix_sideways_chops, 1, +"Fix sideways chop problem"); +extern INT_VAR_H (tessedit_dangambigs_chop, FALSE, +"Use DangAmbigs to direct chop"); +extern INT_VAR_H (tessedit_dangambigs_assoc, FALSE, +"Use DangAmbigs to direct assoc"); + +extern IMAGE page_image; //image of page +extern FILE *debug_fp; //write debug stuff here +#endif diff --git a/ccmain/tfacep.h b/ccmain/tfacep.h new file mode 100644 index 0000000000..6bcdd2ff1a --- /dev/null +++ b/ccmain/tfacep.h @@ -0,0 +1,121 @@ +/********************************************************************** + * File: tfacep.h (Formerly tfacep.h) + * Description: Declarations of C functions and C owned data. + * Author: Ray Smith + * Created: Mon Apr 27 12:51:28 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TFACEP_H +#define TFACEP_H + +#include "hosthplb.h" +#include "tessclas.h" +#include "tessarray.h" +#include "tstruct.h" +#include "notdll.h" +#include "choices.h" +#include "oldlist.h" +#include "hyphen.h" +#include "tface.h" +#include "permute.h" +#include "adaptmatch.h" +#include "blobclass.h" +#include "stopper.h" +#include "associate.h" +#include "chop.h" +#include "expandblob.h" +#include "tordvars.h" +#include "metrics.h" +#include "tface.h" +#include "badwords.h" +#include "structures.h" + +#define BLOB_MATCHING_ON +typedef void (*TESS_TESTER) (TBLOB *, BOOL8, char *, INT32, LIST); +typedef LIST (*TESS_MATCHER) (TBLOB *, TBLOB *, TBLOB *, void *, TEXTROW *); + +extern "C" +{ + /* + int start_recog( //Real main in C + int argc, + char *argv[]); + void program_editup2( //afterforking part + int argc, + char** argv); + + int end_recog( //Real main in C + int argc, + char *argv[]); + void set_interactive_pass(); + void set_pass1(); + void set_pass2(); + //ARRAY cc_recog(TWERD*,TESS_CHOICE*,TESS_CHOICE*,TESS_TESTER, + // TESS_TESTER);*/ + //void wo_learn_blob(TBLOB*,TEXTROW*,char*,INT32); + //LIST AdaptiveClassifier(TBLOB*,TBLOB*,TEXTROW*); + //void LearnBlob(TBLOB*,TEXTROW*,char*,INT32); + //TWERD *newword(); + //TBLOB *newblob(); + //TESSLINE *newoutline(); + //EDGEPT *newedgept(); + //void oldedgept(EDGEPT*); + //void destroy_nodes(void*,void (*)(void*)); + //TESS_LIST *append_choice(TESS_LIST*,char*,double,double,char); + //void fix_quotes (char*); + //void record_certainty(double,int); + //int AcceptableResult(A_CHOICE*,A_CHOICE*); + //int AdaptableWord(TWERD*,const char*,const char*); + //void delete_word(TWERD*); + //void free_blob(TBLOB*); + //void add_document_word(A_CHOICE*); + //void AdaptToWord(TWERD*,TEXTROW*,const char*,const char*,const char*); + //void SaveBadWord(const char*,double); + //void free_choice(TESS_CHOICE*); + //TWERD *newword(); + //TBLOB *newblob(); + //void free_blob( //free a blob + // TBLOB *blob); //blob to free + + //int dict_word( const char* ); + + //extern int tess_cn_matching; + //extern int tess_bn_matching; + //extern int last_word_on_line; + extern TEXTROW normalized_row; + //extern TESS_MATCHER blob_matchers[]; + //extern FILE *rawfile; + //extern FILE *textfile; + //extern int character_count; + //extern int word_count; + //extern int enable_assoc; + //extern int chop_enable; + //extern int permute_only_top; + extern int display_ratings; + +}; + +#if 0 +#define strsave(s) \ + ((s) ? \ + ((char*) strcpy ((char*)alloc_string (strlen(s)+1), s)) : \ + (NULL)) +#endif + +#define BOLD_ON "&dB(s3B" +#define BOLD_OFF "&d@(s0B" +#define UNDERLINE_ON "&dD" +#define UNDERLINE_OFF "&d@" +#endif diff --git a/ccmain/tfacepp.cpp b/ccmain/tfacepp.cpp new file mode 100644 index 0000000000..b43d8bdd39 --- /dev/null +++ b/ccmain/tfacepp.cpp @@ -0,0 +1,411 @@ +/********************************************************************** + * File: tfacepp.cpp (Formerly tface++.c) + * Description: C++ side of the C/C++ Tess/Editor interface. + * Author: Ray Smith + * Created: Thu Apr 23 15:39:23 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include "errcode.h" +#include "tessarray.h" +//#include "fxtop.h" +#include "werd.h" +#include "tfacep.h" +#include "tstruct.h" +#include "tfacepp.h" +#include "tessvars.h" +#include "reject.h" + +#define EXTERN + +EXTERN BOOL_VAR (tessedit_override_permuter, TRUE, "According to dict_word"); + +static POLY_MATCHER tess_matcher;//current matcher +static POLY_TESTER tess_tester; //current tester +static POLY_TESTER tess_trainer; //current trainer +static DENORM *tess_denorm; //current denorm +static WERD *tess_word; //current word + +#define MAX_UNDIVIDED_LENGTH 24 +/********************************************************************** + * recog_word + * + * Convert the word to tess form and pass it to the tess segmenter. + * Convert the output back to editor form. + **********************************************************************/ +WERD_CHOICE *recog_word( //recog one owrd + WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + WERD_CHOICE *word_choice; + UINT8 perm_type; + UINT8 real_dict_perm_type; + + if (word->blob_list ()->empty ()) { + word_choice = new WERD_CHOICE ("", 10.0f, -1.0f, TOP_CHOICE_PERM); + raw_choice = new WERD_CHOICE ("", 10.0f, -1.0f, TOP_CHOICE_PERM); + outword = word->poly_copy (denorm->row ()->x_height ()); + } + else + word_choice = recog_word_recursive (word, denorm, matcher, tester, + trainer, testing, raw_choice, + blob_choices, outword); + if ((word_choice->string ().length () != + outword->blob_list ()->length ()) || + (word_choice->string ().length () != blob_choices->length ())) { + tprintf + ("recog_word ASSERT FAIL String:\"%s\"; Strlen=%d; #Blobs=%d; #Choices=%d\n", + word_choice->string ().string (), word_choice->string ().length (), + outword->blob_list ()->length (), blob_choices->length ()); + } + ASSERT_HOST (word_choice->string ().length () == + outword->blob_list ()->length ()); + ASSERT_HOST (word_choice->string ().length () == blob_choices->length ()); + + /* Copy any reject blobs into the outword */ + outword->rej_blob_list ()->deep_copy (word->rej_blob_list ()); + + if (tessedit_override_permuter) { + /* Override the permuter type if a straight dictionary check disagrees. */ + perm_type = word_choice->permuter (); + if ((perm_type != SYSTEM_DAWG_PERM) && + (perm_type != FREQ_DAWG_PERM) && (perm_type != USER_DAWG_PERM)) { + real_dict_perm_type = dict_word (word_choice->string ().string ()); + if (((real_dict_perm_type == SYSTEM_DAWG_PERM) || + (real_dict_perm_type == FREQ_DAWG_PERM) || + (real_dict_perm_type == USER_DAWG_PERM)) && + (alpha_count (word_choice->string ().string ()) > 0)) + word_choice->set_permuter (real_dict_perm_type); + //Use dict perm + } + if (tessedit_rejection_debug && perm_type != word_choice->permuter ()) { + tprintf ("Permuter Type Flipped from %d to %d\n", + perm_type, word_choice->permuter ()); + } + } + assert ((word_choice == NULL) == (raw_choice == NULL)); + return word_choice; +} + + +/********************************************************************** + * recog_word_recursive + * + * Convert the word to tess form and pass it to the tess segmenter. + * Convert the output back to editor form. + **********************************************************************/ + +WERD_CHOICE *recog_word_recursive( //recog one owrd + WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + INT32 initial_blob_choice_len; + INT32 word_length; //no of blobs + STRING word_string; //converted from tess + ARRAY tess_ratings; //tess results + A_CHOICE tess_choice; //best word + A_CHOICE tess_raw; //raw result + TWERD *tessword; //tess format + BLOB_CHOICE_LIST *choice_list; //fake list + //iterator + BLOB_CHOICE_LIST_C_IT choice_it; + + tess_matcher = matcher; //install matcher + tess_tester = testing ? tester : NULL; + tess_trainer = testing ? trainer : NULL; + tess_denorm = denorm; + tess_word = word; + // blob_matchers[1]=call_matcher; + if (word->blob_list ()->length () > MAX_UNDIVIDED_LENGTH) { + return split_and_recog_word (word, denorm, matcher, tester, trainer, + testing, raw_choice, blob_choices, + outword); + } + else { + if (word->flag (W_EOL)) + last_word_on_line = TRUE; + else + last_word_on_line = FALSE; + initial_blob_choice_len = blob_choices->length (); + tessword = make_tess_word (word, NULL); + tess_ratings = cc_recog (tessword, &tess_choice, &tess_raw, + testing + && tester != NULL /* ? call_tester : NULL */ , + testing + && trainer != + NULL /* ? call_train_tester : NULL */ ); + //convert word + outword = make_ed_word (tessword, word); + if (outword == NULL) { + outword = word->poly_copy (denorm->row ()->x_height ()); + } + delete_word(tessword); //get rid of it + //no of blobs + word_length = outword->blob_list ()->length (); + //convert all ratings + convert_choice_lists(tess_ratings, blob_choices); + //copy string + word_string = tess_raw.string; + while (word_string.length () < word_length) + word_string += " "; //pad with blanks + raw_choice = new WERD_CHOICE (word_string.string (), + tess_raw.rating, tess_raw.certainty, + tess_raw.permuter); + word_string = tess_choice.string; + if (word_string.length () > word_length) { + tprintf ("recog_word: Discarded long string \"%s\"\n", + word_string.string ()); + word_string = NULL; //should never happen + } + if (blob_choices->length () - initial_blob_choice_len != word_length) { + word_string = NULL; //force rejection + tprintf ("recog_word: Choices list len:%d; blob lists len:%d\n", + blob_choices->length (), word_length); + //list of lists + choice_it.set_to_list (blob_choices); + while (blob_choices->length () - initial_blob_choice_len < + word_length) { + //get fake one + choice_list = new BLOB_CHOICE_LIST; + //add to list + choice_it.add_to_end (choice_list); + tprintf ("recog_word: Added dummy choice list\n"); + } + while (blob_choices->length () - initial_blob_choice_len > + word_length) { + choice_it.move_to_last (); + //should never happen + delete choice_it.extract (); + tprintf ("recog_word: Deleted choice list\n"); + } + } + while (word_string.length () < word_length) + word_string += " "; //pad with blanks + + assert (raw_choice != NULL); + if (tess_choice.string) + strfree(tess_choice.string); + if (tess_raw.string) + strfree(tess_raw.string); + return new WERD_CHOICE (word_string.string (), + tess_choice.rating, tess_choice.certainty, + tess_choice.permuter); + } +} + + +/********************************************************************** + * split_and_recog_word + * + * Convert the word to tess form and pass it to the tess segmenter. + * Convert the output back to editor form. + **********************************************************************/ + +WERD_CHOICE *split_and_recog_word( //recog one owrd + WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + // INT32 outword1_len; + // INT32 outword2_len; + WERD *first_word; //poly copy of word + WERD *second_word; //fabricated word + WERD *outword2; //2nd output word + PBLOB *blob; + WERD_CHOICE *result; //resturn value + WERD_CHOICE *result2; //output of 2nd word + WERD_CHOICE *raw_choice2; //raw version of 2nd + float gap; //blob gap + float bestgap; //biggest gap + PBLOB_LIST new_blobs; //list of gathered blobs + PBLOB_IT blob_it; + //iterator + PBLOB_IT new_blob_it = &new_blobs; + + first_word = word->poly_copy (denorm->row ()->x_height ()); + blob_it.set_to_list (first_word->blob_list ()); + bestgap = -MAX_INT32; + while (!blob_it.at_last ()) { + blob = blob_it.data (); + //gap to next + gap = blob_it.data_relative (1)->bounding_box ().left () - blob->bounding_box ().right (); + blob_it.forward (); + if (gap > bestgap) { + bestgap = gap; //find biggest + new_blob_it = blob_it; //save position + } + } + //take 2nd half + new_blobs.assign_to_sublist (&new_blob_it, &blob_it); + //make it a word + second_word = new WERD (&new_blobs, 1, NULL); + ASSERT_HOST (word->blob_list ()->length () == + first_word->blob_list ()->length () + + second_word->blob_list ()->length ()); + + result = recog_word_recursive (first_word, denorm, matcher, + tester, trainer, testing, raw_choice, + blob_choices, outword); + delete first_word; //done that one + result2 = recog_word_recursive (second_word, denorm, matcher, + tester, trainer, testing, raw_choice2, + blob_choices, outword2); + delete second_word; //done that too + *result += *result2; //combine ratings + delete result2; + *raw_choice += *raw_choice2; + delete raw_choice2; //finished with it + // outword1_len= outword->blob_list()->length(); + // outword2_len= outword2->blob_list()->length(); + outword->join_on (outword2); //join words + delete outword2; + // if ( outword->blob_list()->length() != outword1_len + outword2_len ) + // tprintf( "Split&Recog: part1len=%d; part2len=%d; combinedlen=%d\n", + // outword1_len, outword2_len, outword->blob_list()->length() ); + // ASSERT_HOST( outword->blob_list()->length() == outword1_len + outword2_len ); + return result; +} + + +/********************************************************************** + * call_matcher + * + * Called from Tess with a blob in tess form. + * Convert the blob to editor form. + * Call the matcher setup by the segmenter in tess_matcher. + * Convert the output choices back to tess form. + **********************************************************************/ + +LIST call_matcher( //call a matcher + TBLOB *ptblob, //previous + TBLOB *tessblob, //blob to match + TBLOB *ntblob, //next + void *, //unused parameter + TEXTROW * //always null anyway + ) { + PBLOB *pblob; //converted blob + PBLOB *blob; //converted blob + PBLOB *nblob; //converted blob + LIST result; //tess output + BLOB_CHOICE *choice; //current choice + char string[2]; //char converted + BLOB_CHOICE_LIST ratings; //matcher result + BLOB_CHOICE_IT it; //iterator + + blob = make_ed_blob (tessblob);//convert blob + if (blob == NULL) + return NULL; //can't do it + pblob = ptblob != NULL ? make_ed_blob (ptblob) : NULL; + nblob = ntblob != NULL ? make_ed_blob (ntblob) : NULL; + (*tess_matcher) (pblob, blob, nblob, tess_word, tess_denorm, ratings); + //match it + delete blob; //don't need that now + if (pblob != NULL) + delete pblob; + if (nblob != NULL) + delete nblob; + it.set_to_list (&ratings); //get list + result = NULL; + string[1] = '\0'; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + choice = it.data (); + string[0] = choice->char_class (); + result = append_choice (result, string, + choice->rating (), choice->certainty (), + choice->config ()); + } + return result; //converted list +} + + +/********************************************************************** + * call_tester + * + * Called from Tess with a blob in tess form. + * Convert the blob to editor form. + * Call the tester setup by the segmenter in tess_tester. + **********************************************************************/ + +void call_tester( //call a tester + TBLOB *tessblob, //blob to test + BOOL8 correct_blob, //true if good + char *text, //source text + INT32 count, //chars in text + LIST result //output of matcher + ) { + PBLOB *blob; //converted blob + BLOB_CHOICE_LIST ratings; //matcher result + + blob = make_ed_blob (tessblob);//convert blob + if (blob == NULL) + return; + //make it right type + convert_choice_list(result, ratings); + if (tess_tester != NULL) + (*tess_tester) (blob, tess_denorm, correct_blob, text, count, &ratings); + delete blob; //don't need that now +} + + +/********************************************************************** + * call_train_tester + * + * Called from Tess with a blob in tess form. + * Convert the blob to editor form. + * Call the trainer setup by the segmenter in tess_trainer. + **********************************************************************/ + +void call_train_tester( //call a tester + TBLOB *tessblob, //blob to test + BOOL8 correct_blob, //true if good + char *text, //source text + INT32 count, //chars in text + LIST result //output of matcher + ) { + PBLOB *blob; //converted blob + BLOB_CHOICE_LIST ratings; //matcher result + + blob = make_ed_blob (tessblob);//convert blob + if (blob == NULL) + return; + //make it right type + convert_choice_list(result, ratings); + if (tess_trainer != NULL) + (*tess_trainer) (blob, tess_denorm, correct_blob, text, count, &ratings); + delete blob; //don't need that now +} diff --git a/ccmain/tfacepp.h b/ccmain/tfacepp.h new file mode 100644 index 0000000000..20c9c21906 --- /dev/null +++ b/ccmain/tfacepp.h @@ -0,0 +1,85 @@ +/********************************************************************** + * File: tfacepp.h (Formerly tface++.h) + * Description: C++ side of the C/C++ Tess/Editor interface. + * Author: Ray Smith + * Created: Thu Apr 23 15:39:23 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TFACEPP_H +#define TFACEPP_H + +#include "varable.h" +#include "tstruct.h" +#include "ratngs.h" +#include "tessclas.h" +#include "notdll.h" + +extern BOOL_VAR_H (tessedit_override_permuter, TRUE, +"According to dict_word"); +WERD_CHOICE *recog_word( //recog one owrd + WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); + //recog one owrd +WERD_CHOICE *recog_word_recursive(WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); + //recog one owrd +WERD_CHOICE *split_and_recog_word(WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); +LIST call_matcher( //call a matcher + TBLOB *ptblob, //previous + TBLOB *tessblob, //blob to match + TBLOB *ntblob, //next + void *, //unused parameter + TEXTROW * //always null anyway + ); +void call_tester( //call a tester + TBLOB *tessblob, //blob to test + BOOL8 correct_blob, //true if good + char *text, //source text + INT32 count, //chars in text + LIST result //output of matcher + ); +void call_train_tester( //call a tester + TBLOB *tessblob, //blob to test + BOOL8 correct_blob, //true if good + char *text, //source text + INT32 count, //chars in text + LIST result //output of matcher + ); +#endif diff --git a/ccmain/tstruct.cpp b/ccmain/tstruct.cpp new file mode 100644 index 0000000000..6d2ebb5bb9 --- /dev/null +++ b/ccmain/tstruct.cpp @@ -0,0 +1,511 @@ +/********************************************************************** + * File: tstruct.cpp (Formerly tstruct.c) + * Description: Code to manipulate the structures of the C++/C interface. + * Author: Ray Smith + * Created: Thu Apr 23 15:49:29 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "tfacep.h" +#include "tstruct.h" +//#include "structures.h" + +static ERRCODE BADFRAGMENTS = "Couldn't find matching fragment ends"; + +ELISTIZE (FRAGMENT) +//extern /*"C"*/ oldoutline(TESSLINE*); +/********************************************************************** + * FRAGMENT::FRAGMENT + * + * Constructor for fragments. + **********************************************************************/ +FRAGMENT::FRAGMENT ( //constructor +EDGEPT * head_pt, //start point +EDGEPT * tail_pt //end point +):head (head_pt->pos.x, head_pt->pos.y), tail (tail_pt->pos.x, +tail_pt->pos.y) { + headpt = head_pt; //save ptrs + tailpt = tail_pt; +} + + +/********************************************************************** + * make_ed_word + * + * Make an editor format word from the tess style word. + **********************************************************************/ + +WERD *make_ed_word( //construct word + TWERD *tessword, //word to convert + WERD *clone //clone this one + ) { + WERD *word; //converted word + TBLOB *tblob; //current blob + PBLOB *blob; //new blob + PBLOB_LIST blobs; //list of blobs + PBLOB_IT blob_it = &blobs; //iterator + + for (tblob = tessword->blobs; tblob != NULL; tblob = tblob->next) { + blob = make_ed_blob (tblob); + if (blob != NULL) + blob_it.add_after_then_move (blob); + } + if (!blobs.empty ()) + word = new WERD (&blobs, clone); + else + word = NULL; + return word; +} + + +/********************************************************************** + * make_ed_blob + * + * Make an editor format blob from the tess style blob. + **********************************************************************/ + +PBLOB *make_ed_blob( //construct blob + TBLOB *tessblob //blob to convert + ) { + TESSLINE *tessol; //tess outline + FRAGMENT_LIST fragments; //list of fragments + OUTLINE *outline; //current outline + OUTLINE_LIST out_list; //list of outlines + OUTLINE_IT out_it = &out_list; //iterator + + for (tessol = tessblob->outlines; tessol != NULL; tessol = tessol->next) { + //stick in list + register_outline(tessol, &fragments); + } + while (!fragments.empty ()) { + outline = make_ed_outline (&fragments); + if (outline != NULL) + out_it.add_after_then_move (outline); + } + if (out_it.empty()) + return NULL; //couldn't do it + return new PBLOB (&out_list); //turn to blob +} + + +/********************************************************************** + * make_ed_outline + * + * Make an editor format outline from the list of fragments. + **********************************************************************/ + +OUTLINE *make_ed_outline( //constructoutline + FRAGMENT_LIST *list //list of fragments + ) { + FRAGMENT *fragment; //current fragment + EDGEPT *edgept; //current point + ICOORD headpos; //coords of head + ICOORD tailpos; //coords of tail + FCOORD pos; //coords of edgept + FCOORD vec; //empty + POLYPT *polypt; //current point + POLYPT_LIST poly_list; //list of point + POLYPT_IT poly_it = &poly_list;//iterator + FRAGMENT_IT fragment_it = list;//fragment + + headpos = fragment_it.data ()->head; + do { + fragment = fragment_it.data (); + edgept = fragment->headpt; //start of segment + do { + pos = FCOORD (edgept->pos.x, edgept->pos.y); + vec = FCOORD (edgept->vec.x, edgept->vec.y); + polypt = new POLYPT (pos, vec); + //add to list + poly_it.add_after_then_move (polypt); + edgept = edgept->next; + } + while (edgept != fragment->tailpt); + tailpos = ICOORD (edgept->pos.x, edgept->pos.y); + //get rid of it + delete fragment_it.extract (); + if (tailpos != headpos) { + if (fragment_it.empty ()) { + // tprintf("Bad tailpos (%d,%d), Head=(%d,%d), no fragments.\n", + // fragment->head.x(),fragment->head.y(), + // headpos.x(),headpos.y()); + return NULL; + } + fragment_it.forward (); + //find next segment + for (fragment_it.mark_cycle_pt (); !fragment_it.cycled_list () && fragment_it.data ()->head != tailpos; + fragment_it.forward ()); + if (fragment_it.data ()->head != tailpos) { + // tprintf("Bad tailpos (%d,%d), Fragments are:\n", + // tailpos.x(),tailpos.y()); + for (fragment_it.mark_cycle_pt (); + !fragment_it.cycled_list (); fragment_it.forward ()) { + fragment = fragment_it.extract (); + // tprintf("Head=(%d,%d), tail=(%d,%d)\n", + // fragment->head.x(),fragment->head.y(), + // fragment->tail.x(),fragment->tail.y()); + delete fragment; + } + return NULL; //can't do it + // BADFRAGMENTS.error("make_ed_blob",ABORT,NULL); + } + } + } + while (tailpos != headpos); + return new OUTLINE (&poly_it); //turn to outline +} + + +/********************************************************************** + * register_outline + * + * Add the fragments in the given outline to the list + **********************************************************************/ + +void register_outline( //add fragments + TESSLINE *outline, //tess format + FRAGMENT_LIST *list //list to add to + ) { + EDGEPT *startpt; //start of outline + EDGEPT *headpt; //start of fragment + EDGEPT *tailpt; //end of fragment + FRAGMENT *fragment; //new fragment + FRAGMENT_IT it = list; //iterator + + startpt = outline->loop; + do { + startpt = startpt->next; + if (startpt == NULL) + return; //illegal! + } + while (startpt->flags[0] == 0 && startpt != outline->loop); + headpt = startpt; + do + startpt = startpt->next; + while (startpt->flags[0] != 0 && startpt != headpt); + if (startpt->flags[0] != 0) + return; //all hidden! + + headpt = startpt; + do { + tailpt = headpt; + do + tailpt = tailpt->next; + while (tailpt->flags[0] == 0 && tailpt != startpt); + fragment = new FRAGMENT (headpt, tailpt); + it.add_after_then_move (fragment); + while (tailpt->flags[0] != 0) + tailpt = tailpt->next; + headpt = tailpt; + } + while (tailpt != startpt); +} + + +/********************************************************************** + * convert_choice_lists + * + * Convert the ARRAY of TESS_LIST of TESS_CHOICEs into a BLOB_CHOICE_LIST. + **********************************************************************/ + +void convert_choice_lists( //convert lists + ARRAY tessarray, //list from tess + BLOB_CHOICE_LIST_CLIST *ratings //list of results + ) { + INT32 length; //elements in array + INT32 index; //index to array + LIST result; //tess output + //iterator + BLOB_CHOICE_LIST_C_IT it = ratings; + BLOB_CHOICE_LIST *choice; //created choice + + if (tessarray != NULL) { + length = array_count (tessarray); + for (index = 0; index < length; index++) { + result = (LIST) array_value (tessarray, index); + //make one + choice = new BLOB_CHOICE_LIST; + //convert blob choices + convert_choice_list(result, *choice); + //add to super list + it.add_after_then_move (choice); + } + free_mem(tessarray); //lists already freed + } +} + + +/********************************************************************** + * convert_choice_list + * + * Convert the LIST of TESS_CHOICEs into a BLOB_CHOICE_LIST. + **********************************************************************/ + +void convert_choice_list( //convert lists + LIST list, //list from tess + BLOB_CHOICE_LIST &ratings //list of results + ) { + LIST result; //tess output + BLOB_CHOICE_IT it = &ratings; //iterator + BLOB_CHOICE *choice; //created choice + A_CHOICE *tesschoice; //choice to convert + + for (result = list; result != NULL; result = result->next) { + //traverse list + tesschoice = (A_CHOICE *) result->node; + //make one + choice = new BLOB_CHOICE (tesschoice->string[0], tesschoice->rating, tesschoice->certainty, tesschoice->config); + it.add_after_then_move (choice); + } + destroy_nodes (list, (void (*)(void *)) free_choice); + //get rid of it +} + + +/********************************************************************** + * make_tess_row + * + * Make a fake row structure to pass to the tesseract matchers. + **********************************************************************/ + +void make_tess_row( //make fake row + DENORM *denorm, //row info + TEXTROW *tessrow //output row + ) { + tessrow->baseline.segments = 1; + tessrow->baseline.xstarts[0] = -32767; + tessrow->baseline.xstarts[1] = 32767; + tessrow->baseline.quads[0].a = 0; + tessrow->baseline.quads[0].b = 0; + tessrow->baseline.quads[0].c = bln_baseline_offset; + tessrow->xheight.segments = 1; + tessrow->xheight.xstarts[0] = -32767; + tessrow->xheight.xstarts[1] = 32767; + tessrow->xheight.quads[0].a = 0; + tessrow->xheight.quads[0].b = 0; + tessrow->xheight.quads[0].c = bln_x_height + bln_baseline_offset; + tessrow->lineheight = bln_x_height; + tessrow->ascrise = denorm->row ()->ascenders () * denorm->scale (); + tessrow->descdrop = denorm->row ()->descenders () * denorm->scale (); +} + + +/********************************************************************** + * make_tess_word + * + * Convert the word to Tess format. + **********************************************************************/ + +TWERD *make_tess_word( //convert owrd + WERD *word, //word to do + TEXTROW *row //fake row + ) { + TWERD *tessword; //tess format + + tessword = newword (); //use old allocator + tessword->row = row; //give them something + //copy string + tessword->correct = strsave (word->text ()); + tessword->guess = NULL; + tessword->blobs = make_tess_blobs (word->blob_list ()); + tessword->blanks = 1; + tessword->blobcount = word->blob_list ()->length (); + tessword->next = NULL; + return tessword; +} + + +/********************************************************************** + * make_tess_blobs + * + * Make Tess style blobs from a list of BLOBs. + **********************************************************************/ + +TBLOB *make_tess_blobs( //make tess blobs + PBLOB_LIST *bloblist //list to convert + ) { + PBLOB_IT it = bloblist; //iterator + PBLOB *blob; //current blob + TBLOB *head; //output list + TBLOB *tail; //end of list + TBLOB *tessblob; + + head = NULL; + tail = NULL; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + blob = it.data (); + tessblob = make_tess_blob (blob, TRUE); + if (head) + tail->next = tessblob; + else + head = tessblob; + tail = tessblob; + } + return head; +} + + +/********************************************************************** + * make_tess_blob + * + * Make a single Tess style blob + **********************************************************************/ + +TBLOB *make_tess_blob( //make tess blob + PBLOB *blob, //blob to convert + BOOL8 flatten //flatten outline structure + ) { + INT32 index; + TBLOB *tessblob; + + tessblob = newblob (); + tessblob->outlines = (struct olinestruct *) + make_tess_outlines (blob->out_list (), flatten); + for (index = 0; index < TBLOBFLAGS; index++) + tessblob->flags[index] = 0; //!! + tessblob->correct = 0; + tessblob->guess = 0; + for (index = 0; index < MAX_WO_CLASSES; index++) { + tessblob->classes[index] = 0; + tessblob->values[index] = 0; + } + tessblob->next = NULL; + return tessblob; +} + + +/********************************************************************** + * make_tess_outlines + * + * Make Tess style outlines from a list of OUTLINEs. + **********************************************************************/ + +TESSLINE *make_tess_outlines( //make tess outlines + OUTLINE_LIST *outlinelist, //list to convert + BOOL8 flatten //flatten outline structure + ) { + OUTLINE_IT it = outlinelist; //iterator + OUTLINE *outline; //current outline + TESSLINE *head; //output list + TESSLINE *tail; //end of list + TESSLINE *tessoutline; + + head = NULL; + tail = NULL; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + tessoutline = newoutline (); + tessoutline->compactloop = NULL; + tessoutline->loop = make_tess_edgepts (outline->polypts (), + tessoutline->topleft, + tessoutline->botright); + if (tessoutline->loop == NULL) { + oldoutline(tessoutline); + continue; + } + tessoutline->start = tessoutline->loop->pos; + tessoutline->node = NULL; + tessoutline->next = NULL; + tessoutline->child = NULL; + if (!outline->child ()->empty ()) { + if (flatten) + tessoutline->next = (struct olinestruct *) + make_tess_outlines (outline->child (), flatten); + else { + tessoutline->next = NULL; + tessoutline->child = (struct olinestruct *) + make_tess_outlines (outline->child (), flatten); + } + } + else + tessoutline->next = NULL; + if (head) + tail->next = tessoutline; + else + head = tessoutline; + while (tessoutline->next != NULL) + tessoutline = tessoutline->next; + tail = tessoutline; + } + return head; +} + + +/********************************************************************** + * make_tess_edgepts + * + * Make Tess style edgepts from a list of POLYPTs. + **********************************************************************/ + +EDGEPT *make_tess_edgepts( //make tess edgepts + POLYPT_LIST *edgeptlist, //list to convert + TPOINT &tl, //bounding box + TPOINT &br) { + INT32 index; + POLYPT_IT it = edgeptlist; //iterator + POLYPT *edgept; //current edgept + EDGEPT *head; //output list + EDGEPT *tail; //end of list + EDGEPT *tessedgept; + + head = NULL; + tail = NULL; + tl.x = MAX_INT16; + tl.y = -MAX_INT16; + br.x = -MAX_INT16; + br.y = MAX_INT16; + for (it.mark_cycle_pt (); !it.cycled_list ();) { + edgept = it.data (); + tessedgept = newedgept (); + tessedgept->pos.x = (INT16) edgept->pos.x (); + tessedgept->pos.y = (INT16) edgept->pos.y (); + if (tessedgept->pos.x < tl.x) + tl.x = tessedgept->pos.x; + if (tessedgept->pos.x > br.x) + br.x = tessedgept->pos.x; + if (tessedgept->pos.y > tl.y) + tl.y = tessedgept->pos.y; + if (tessedgept->pos.y < br.y) + br.y = tessedgept->pos.y; + if (head != NULL && tessedgept->pos.x == tail->pos.x + && tessedgept->pos.y == tail->pos.y) { + oldedgept(tessedgept); + } + else { + for (index = 0; index < EDGEPTFLAGS; index++) + tessedgept->flags[index] = 0; + if (head != NULL) { + tail->vec.x = tessedgept->pos.x - tail->pos.x; + tail->vec.y = tessedgept->pos.y - tail->pos.y; + tessedgept->prev = tail; + } + tessedgept->next = head; + if (head) + tail->next = tessedgept; + else + head = tessedgept; + tail = tessedgept; + } + it.forward (); + } + head->prev = tail; + tail->vec.x = head->pos.x - tail->pos.x; + tail->vec.y = head->pos.y - tail->pos.y; + if (head == tail) { + oldedgept(head); + return NULL; //empty + } + return head; +} diff --git a/ccmain/tstruct.h b/ccmain/tstruct.h new file mode 100644 index 0000000000..1a4ae7796c --- /dev/null +++ b/ccmain/tstruct.h @@ -0,0 +1,108 @@ +/********************************************************************** + * File: tstruct.h (Formerly tstruct.h) + * Description: Code to manipulate the structures of the C++/C interface. + * Author: Ray Smith + * Created: Thu Apr 23 15:49:29 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TSTRUCT_H +#define TSTRUCT_H + +#include "tessarray.h" +#include "werd.h" +#include "tessclas.h" +#include "ratngs.h" +#include "notdll.h" +#include "oldlist.h" + +/* +struct TESS_LIST +{ + TESS_LIST *node; //data + TESS_LIST *next; //next in list +}; + +struct TESS_CHOICE +{ + float rating; //scaled + float certainty; //absolute + char permuter; //which permuter code + INT8 config; //which config + char* string; //really can! +}; +*/ +class FRAGMENT:public ELIST_LINK +{ + public: + FRAGMENT() { //constructor + } + FRAGMENT(EDGEPT *head_pt, //start + EDGEPT *tail_pt); //end + + ICOORD head; //coords of start + ICOORD tail; //coords of end + EDGEPT *headpt; //start point + EDGEPT *tailpt; //end point + + NEWDELETE2 (FRAGMENT) +}; + +ELISTIZEH (FRAGMENT) +WERD *make_ed_word( //construct word + TWERD *tessword, //word to convert + WERD *clone //clone this one + ); +PBLOB *make_ed_blob( //construct blob + TBLOB *tessblob //blob to convert + ); +OUTLINE *make_ed_outline( //constructoutline + FRAGMENT_LIST *list //list of fragments + ); +void register_outline( //add fragments + TESSLINE *outline, //tess format + FRAGMENT_LIST *list //list to add to + ); +void convert_choice_lists( //convert lists + ARRAY tessarray, //list from tess + BLOB_CHOICE_LIST_CLIST *ratings //list of results + ); +void convert_choice_list( //convert lists + LIST list, //list from tess + BLOB_CHOICE_LIST &ratings //list of results + ); +void make_tess_row( //make fake row + DENORM *denorm, //row info + TEXTROW *tessrow //output row + ); +TWERD *make_tess_word( //convert owrd + WERD *word, //word to do + TEXTROW *row //fake row + ); +TBLOB *make_tess_blobs( //make tess blobs + PBLOB_LIST *bloblist //list to convert + ); +TBLOB *make_tess_blob( //make tess blob + PBLOB *blob, //blob to convert + BOOL8 flatten //flatten outline structure + ); +TESSLINE *make_tess_outlines( //make tess outlines + OUTLINE_LIST *outlinelist, //list to convert + BOOL8 flatten //flatten outline structure + ); +EDGEPT *make_tess_edgepts( //make tess edgepts + POLYPT_LIST *edgeptlist, //list to convert + TPOINT &tl, //bounding box + TPOINT &br); +#endif diff --git a/ccmain/werdit.cpp b/ccmain/werdit.cpp new file mode 100644 index 0000000000..299809e061 --- /dev/null +++ b/ccmain/werdit.cpp @@ -0,0 +1,193 @@ +/********************************************************************** + * File: werdit.cpp (Formerly wordit.c) + * Description: An iterator for passing over all the words in a document. + * Author: Ray Smith + * Created: Mon Apr 27 08:51:22 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "werdit.h" + +#define EXTERN + +//EXTERN BOOL_VAR(wordit_linearc,FALSE,"Pass poly of linearc to Tess"); + +/********************************************************************** + * WERDIT::start_page + * + * Get ready to iterate over the page by setting the iterators. + **********************************************************************/ + +void WERDIT::start_page( //set iterators + BLOCK_LIST *block_list //blocks to check + ) { + block_it.set_to_list (block_list); + block_it.mark_cycle_pt (); + do { + while (block_it.data ()->row_list ()->empty () + && !block_it.cycled_list ()) { + block_it.forward (); + } + if (!block_it.data ()->row_list ()->empty ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + row_it.mark_cycle_pt (); + while (row_it.data ()->word_list ()->empty () + && !row_it.cycled_list ()) { + row_it.forward (); + } + if (!row_it.data ()->word_list ()->empty ()) { + word_it.set_to_list (row_it.data ()->word_list ()); + word_it.mark_cycle_pt (); + } + } + } + while (!block_it.cycled_list () && row_it.data ()->word_list ()->empty ()); +} + + +/********************************************************************** + * WERDIT::forward + * + * Give the next word on the page, or NULL if none left. + * This code assumes all rows to be non-empty, but blocks are allowed + * to be empty as eventually we will have non-text blocks. + * The output is always a copy and needs to be deleted by somebody. + **********************************************************************/ + +WERD *WERDIT::forward() { //use iterators + WERD *word; //actual word + // WERD *larc_word; //linearc copy + WERD *result; //output word + ROW *row; //row of word + + if (word_it.cycled_list ()) { + return NULL; //finished page + } + else { + word = word_it.data (); + row = row_it.data (); + word_it.forward (); + if (word_it.cycled_list ()) { + row_it.forward (); //finished row + if (row_it.cycled_list ()) { + do { + block_it.forward (); //finished block + if (!block_it.cycled_list ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + row_it.mark_cycle_pt (); + } + } + //find non-empty block + while (!block_it.cycled_list () + && row_it.cycled_list ()); + } + if (!row_it.cycled_list ()) { + word_it.set_to_list (row_it.data ()->word_list ()); + word_it.mark_cycle_pt (); + } + } + + // if (wordit_linearc && !word->flag(W_POLYGON)) + // { + // larc_word=word->larc_copy(row->x_height()); + // result=larc_word->poly_copy(row->x_height()); + // delete larc_word; + // } + // else + result = word->poly_copy (row->x_height ()); + return result; + } +} + + +/********************************************************************** + * make_pseudo_word + * + * Make all the blobs inside a selection into a single word. + * The word is always a copy and needs to be deleted. + **********************************************************************/ + +WERD *make_pseudo_word( //make fake word + BLOCK_LIST *block_list, //blocks to check //block of selection + BOX &selection_box, + BLOCK *&pseudo_block, + ROW *&pseudo_row //row of selection + ) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + PBLOB_LIST new_blobs; //list of gathered blobs + //iterator + PBLOB_IT new_blob_it = &new_blobs; + WERD *pseudo_word; //fabricated word + WERD *poly_word; //poly copy of word + // WERD *larc_word; //linearc copy + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + pseudo_block = block; + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->bounding_box ().overlap (selection_box)) { + // if (wordit_linearc && !word->flag(W_POLYGON)) + // { + // larc_word=word->larc_copy(row->x_height()); + // poly_word=larc_word->poly_copy(row->x_height()); + // delete larc_word; + // } + // else + poly_word = word->poly_copy (row->x_height ()); + blob_it.set_to_list (poly_word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (blob->bounding_box (). + overlap (selection_box)) { + new_blob_it.add_after_then_move (blob_it. + extract + ()); + //steal off list + pseudo_row = row; + } + } + delete poly_word; //get rid of it + } + } + } + } + } + } + if (!new_blobs.empty ()) { + //make new word + pseudo_word = new WERD (&new_blobs, 1, NULL); + } + else + pseudo_word = NULL; + return pseudo_word; +} diff --git a/ccmain/werdit.h b/ccmain/werdit.h new file mode 100644 index 0000000000..0c6050bb8a --- /dev/null +++ b/ccmain/werdit.h @@ -0,0 +1,67 @@ +/********************************************************************** + * File: wordit.c + * Description: An iterator for passing over all the words in a document. + * Author: Ray Smith + * Created: Mon Apr 27 08:51:22 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef WERDIT_H +#define WERDIT_H + +#include "varable.h" +#include "ocrblock.h" +#include "notdll.h" + +class WERDIT +{ + public: + WERDIT() { + } //empty contructor + WERDIT( //empty contructor + BLOCK_LIST *blocklist) { //blocks on page + start_page(blocklist); //ready to scan + } + + void start_page( //get ready + BLOCK_LIST *blocklist); //blocks on page + + WERD *forward(); //get next word + WERD *next_word() { //get next word + return word_it.data (); //already at next + } + ROW *row() { //get current row + return word_it.cycled_list ()? NULL : row_it.data (); + } + ROW *next_row() { //get next row + return row_it.data_relative (1); + } + BLOCK *block() { //get current block + return block_it.data (); + } + + private: + BLOCK_IT block_it; //iterators + ROW_IT row_it; + WERD_IT word_it; +}; + +//extern BOOL_VAR_H(wordit_linearc,FALSE,"Pass poly of linearc to Tess"); +WERD *make_pseudo_word( //make fake word + BLOCK_LIST *block_list, //blocks to check //block of selection + BOX &selection_box, + BLOCK *&pseudo_block, + ROW *&pseudo_row //row of selection + ); +#endif diff --git a/ccstruct/Makefile.am b/ccstruct/Makefile.am new file mode 100644 index 0000000000..50fdaddcd7 --- /dev/null +++ b/ccstruct/Makefile.am @@ -0,0 +1,25 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccutil -I$(top_srcdir)/cutil \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + blckerr.h blobbox.h blobs.h blread.h coutln.h crakedge.h \ + genblob.h hpddef.h hpdsizes.h ipoints.h labls.h linlsq.h \ + lmedsq.h mod128.h normalis.h ocrblock.h ocrrow.h pageblk.h \ + pageres.h pdblock.h pdclass.h points.h polyaprx.h polyblk.h \ + polyblob.h polyvert.h poutline.h quadlsq.h quadratc.h \ + quspline.h ratngs.h rect.h rejctmap.h rwpoly.h statistc.h \ + stepblob.h txtregn.h vecfuncs.h werd.h + +noinst_LIBRARIES = libtesseract_ccstruct.a +libtesseract_ccstruct_a_SOURCES = \ + blobbox.cpp blobs.cpp blread.cpp callcpp.cpp \ + coutln.cpp genblob.cpp labls.cpp linlsq.cpp \ + lmedsq.cpp mod128.cpp normalis.cpp ocrblock.cpp \ + ocrrow.cpp pageblk.cpp pageres.cpp pdblock.cpp \ + points.cpp polyaprx.cpp polyblk.cpp polyblob.cpp \ + polyvert.cpp poutline.cpp quadlsq.cpp quadratc.cpp \ + quspline.cpp ratngs.cpp rect.cpp rejctmap.cpp \ + rwpoly.cpp statistc.cpp stepblob.cpp txtregn.cpp \ + vecfuncs.cpp werd.cpp diff --git a/ccstruct/Makefile.in b/ccstruct/Makefile.in new file mode 100644 index 0000000000..0259918444 --- /dev/null +++ b/ccstruct/Makefile.in @@ -0,0 +1,587 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = ccstruct +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_ccstruct_a_AR = $(AR) $(ARFLAGS) +libtesseract_ccstruct_a_LIBADD = +am_libtesseract_ccstruct_a_OBJECTS = blobbox.$(OBJEXT) blobs.$(OBJEXT) \ + blread.$(OBJEXT) callcpp.$(OBJEXT) coutln.$(OBJEXT) \ + genblob.$(OBJEXT) labls.$(OBJEXT) linlsq.$(OBJEXT) \ + lmedsq.$(OBJEXT) mod128.$(OBJEXT) normalis.$(OBJEXT) \ + ocrblock.$(OBJEXT) ocrrow.$(OBJEXT) pageblk.$(OBJEXT) \ + pageres.$(OBJEXT) pdblock.$(OBJEXT) points.$(OBJEXT) \ + polyaprx.$(OBJEXT) polyblk.$(OBJEXT) polyblob.$(OBJEXT) \ + polyvert.$(OBJEXT) poutline.$(OBJEXT) quadlsq.$(OBJEXT) \ + quadratc.$(OBJEXT) quspline.$(OBJEXT) ratngs.$(OBJEXT) \ + rect.$(OBJEXT) rejctmap.$(OBJEXT) rwpoly.$(OBJEXT) \ + statistc.$(OBJEXT) stepblob.$(OBJEXT) txtregn.$(OBJEXT) \ + vecfuncs.$(OBJEXT) werd.$(OBJEXT) +libtesseract_ccstruct_a_OBJECTS = \ + $(am_libtesseract_ccstruct_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_ccstruct_a_SOURCES) +DIST_SOURCES = $(libtesseract_ccstruct_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccutil -I$(top_srcdir)/cutil \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + blckerr.h blobbox.h blobs.h blread.h coutln.h crakedge.h \ + genblob.h hpddef.h hpdsizes.h ipoints.h labls.h linlsq.h \ + lmedsq.h mod128.h normalis.h ocrblock.h ocrrow.h pageblk.h \ + pageres.h pdblock.h pdclass.h points.h polyaprx.h polyblk.h \ + polyblob.h polyvert.h poutline.h quadlsq.h quadratc.h \ + quspline.h ratngs.h rect.h rejctmap.h rwpoly.h statistc.h \ + stepblob.h txtregn.h vecfuncs.h werd.h + +noinst_LIBRARIES = libtesseract_ccstruct.a +libtesseract_ccstruct_a_SOURCES = \ + blobbox.cpp blobs.cpp blread.cpp callcpp.cpp \ + coutln.cpp genblob.cpp labls.cpp linlsq.cpp \ + lmedsq.cpp mod128.cpp normalis.cpp ocrblock.cpp \ + ocrrow.cpp pageblk.cpp pageres.cpp pdblock.cpp \ + points.cpp polyaprx.cpp polyblk.cpp polyblob.cpp \ + polyvert.cpp poutline.cpp quadlsq.cpp quadratc.cpp \ + quspline.cpp ratngs.cpp rect.cpp rejctmap.cpp \ + rwpoly.cpp statistc.cpp stepblob.cpp txtregn.cpp \ + vecfuncs.cpp werd.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu ccstruct/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu ccstruct/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_ccstruct.a: $(libtesseract_ccstruct_a_OBJECTS) $(libtesseract_ccstruct_a_DEPENDENCIES) + -rm -f libtesseract_ccstruct.a + $(libtesseract_ccstruct_a_AR) libtesseract_ccstruct.a $(libtesseract_ccstruct_a_OBJECTS) $(libtesseract_ccstruct_a_LIBADD) + $(RANLIB) libtesseract_ccstruct.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobbox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blread.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callcpp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/coutln.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/genblob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/labls.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linlsq.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmedsq.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod128.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normalis.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrrow.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pageblk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pageres.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/points.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyaprx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyblk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyblob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyvert.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/poutline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quadlsq.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quadratc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quspline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ratngs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rect.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rejctmap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rwpoly.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statistc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stepblob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/txtregn.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vecfuncs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/werd.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ccstruct/blckerr.h b/ccstruct/blckerr.h new file mode 100644 index 0000000000..e306163983 --- /dev/null +++ b/ccstruct/blckerr.h @@ -0,0 +1,29 @@ +/********************************************************************** + * File: blckerr.h (Formerly blockerr.h) + * Description: Error codes for the page block classes. + * Author: Ray Smith + * Created: Tue Mar 19 17:43:30 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BLCKERR_H +#define BLCKERR_H + +#include "errcode.h" + +const ERRCODE BADBLOCKLINE = "Y coordinate in block out of bounds"; +const ERRCODE LOSTBLOCKLINE = "Can't find rectangle for line"; +const ERRCODE ILLEGAL_GRADIENT = "Gradient wrong side of edge step!"; +const ERRCODE WRONG_WORD = "Word doesn't have blobs of that type"; +#endif diff --git a/ccstruct/blobbox.cpp b/ccstruct/blobbox.cpp new file mode 100644 index 0000000000..6e25ba0f07 --- /dev/null +++ b/ccstruct/blobbox.cpp @@ -0,0 +1,778 @@ +/********************************************************************** + * File: blobbox.cpp (Formerly blobnbox.c) + * Description: Code for the textord blob class. + * Author: Ray Smith + * Created: Thu Jul 30 09:08:51 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "blobbox.h" + +#define PROJECTION_MARGIN 10 //arbitrary +#define EXTERN + +EXTERN double_VAR (textord_error_weight, 3, +"Weighting for error in believability"); +EXTERN BOOL_VAR (pitsync_projection_fix, TRUE, +"Fix bug in projection profile"); + +ELISTIZE (BLOBNBOX) ELIST2IZE (TO_ROW) ELISTIZE (TO_BLOCK) +/********************************************************************** + * BLOBNBOX::merge + * + * Merge this blob with the given blob, which should be after this. + **********************************************************************/ +void BLOBNBOX::merge( //merge blobs + BLOBNBOX *nextblob //blob to join with + ) { + box += nextblob->box; //merge boxes + nextblob->joined = TRUE; +} + + +/********************************************************************** + * BLOBNBOX::chop + * + * Chop this blob into equal sized pieces using the x height as a guide. + * The blob is not actually chopped. Instead, fake blobs are inserted + * with the relevant bounding boxes. + **********************************************************************/ + +void BLOBNBOX::chop( //chop blobs + BLOBNBOX_IT *start_it, //location of this + BLOBNBOX_IT *end_it, //iterator + FCOORD rotation, //for landscape + float xheight //of line + ) { + INT16 blobcount; //no of blobs + BLOBNBOX *newblob; //fake blob + BLOBNBOX *blob; //current blob + INT16 blobindex; //number of chop + INT16 leftx; //left edge of blob + float blobwidth; //width of each + float rightx; //right edge to scan + float ymin, ymax; //limits of new blob + float test_ymin, test_ymax; //limits of part blob + ICOORD bl, tr; //corners of box + BLOBNBOX_IT blob_it; //blob iterator + + //get no of chops + blobcount = (INT16) floor (box.width () / xheight); + if (blobcount > 1 && (blob_ptr != NULL || cblob_ptr != NULL)) { + //width of each + blobwidth = (float) (box.width () + 1) / blobcount; + for (blobindex = blobcount - 1, rightx = box.right (); + blobindex >= 0; blobindex--, rightx -= blobwidth) { + ymin = (float) MAX_INT32; + ymax = (float) -MAX_INT32; + blob_it = *start_it; + do { + blob = blob_it.data (); + if (blob->blob_ptr != NULL) + find_blob_limits (blob->blob_ptr, rightx - blobwidth, rightx, + rotation, test_ymin, test_ymax); + else + find_cblob_vlimits (blob->cblob_ptr, rightx - blobwidth, + rightx, + /*rotation, */ test_ymin, test_ymax); + blob_it.forward (); + if (test_ymin < ymin) + ymin = test_ymin; + if (test_ymax > ymax) + ymax = test_ymax; + } + while (blob != end_it->data ()); + if (ymin < ymax) { + leftx = (INT16) floor (rightx - blobwidth); + if (leftx < box.left ()) + leftx = box.left (); //clip to real box + bl = ICOORD (leftx, (INT16) floor (ymin)); + tr = ICOORD ((INT16) ceil (rightx), (INT16) ceil (ymax)); + if (blobindex == 0) + box = BOX (bl, tr); //change box + else { + newblob = new BLOBNBOX; + //box is all it has + newblob->box = BOX (bl, tr); + //stay on current + end_it->add_after_stay_put (newblob); + } + } + } + } +} + + +/********************************************************************** + * find_blob_limits + * + * Scan the outlines of the blob to locate the y min and max + * between the given x limits. + **********************************************************************/ + +void find_blob_limits( //get y limits + PBLOB *blob, //blob to search + float leftx, //x limits + float rightx, + FCOORD rotation, //for landscape + float &ymin, //output y limits + float &ymax) { + float testy; //y intercept + FCOORD pos; //rotated + FCOORD vec; + POLYPT *polypt; //current point + //outlines + OUTLINE_IT out_it = blob->out_list (); + POLYPT_IT poly_it; //outline pts + + ymin = (float) MAX_INT32; + ymax = (float) -MAX_INT32; + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + //get points + poly_it.set_to_list (out_it.data ()->polypts ()); + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); + poly_it.forward ()) { + polypt = poly_it.data (); + pos = polypt->pos; + pos.rotate (rotation); + vec = polypt->vec; + vec.rotate (rotation); + if (pos.x () < leftx && pos.x () + vec.x () > leftx + || pos.x () > leftx && pos.x () + vec.x () < leftx) { + testy = pos.y () + vec.y () * (leftx - pos.x ()) / vec.x (); + //intercept of boundary + if (testy < ymin) + ymin = testy; + if (testy > ymax) + ymax = testy; + } + if (pos.x () >= leftx && pos.x () <= rightx) { + if (pos.y () > ymax) + ymax = pos.y (); + if (pos.y () < ymin) + ymin = pos.y (); + } + if (pos.x () > rightx && pos.x () + vec.x () < rightx + || pos.x () < rightx && pos.x () + vec.x () > rightx) { + testy = pos.y () + vec.y () * (rightx - pos.x ()) / vec.x (); + //intercept of boundary + if (testy < ymin) + ymin = testy; + if (testy > ymax) + ymax = testy; + } + } + } +} + + +/********************************************************************** + * find_cblob_limits + * + * Scan the outlines of the cblob to locate the y min and max + * between the given x limits. + **********************************************************************/ + +void find_cblob_limits( //get y limits + C_BLOB *blob, //blob to search + float leftx, //x limits + float rightx, + FCOORD rotation, //for landscape + float &ymin, //output y limits + float &ymax) { + INT16 stepindex; //current point + ICOORD pos; //current coords + ICOORD vec; //rotated step + C_OUTLINE *outline; //current outline + //outlines + C_OUTLINE_IT out_it = blob->out_list (); + + ymin = (float) MAX_INT32; + ymax = (float) -MAX_INT32; + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + outline = out_it.data (); + pos = outline->start_pos (); //get coords + pos.rotate (rotation); + for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { + //inside + if (pos.x () >= leftx && pos.x () <= rightx) { + if (pos.y () > ymax) + ymax = pos.y (); + if (pos.y () < ymin) + ymin = pos.y (); + } + vec = outline->step (stepindex); + vec.rotate (rotation); + pos += vec; //move to next + } + } +} + + +/********************************************************************** + * find_cblob_vlimits + * + * Scan the outlines of the cblob to locate the y min and max + * between the given x limits. + **********************************************************************/ + +void find_cblob_vlimits( //get y limits + C_BLOB *blob, //blob to search + float leftx, //x limits + float rightx, + float &ymin, //output y limits + float &ymax) { + INT16 stepindex; //current point + ICOORD pos; //current coords + ICOORD vec; //rotated step + C_OUTLINE *outline; //current outline + //outlines + C_OUTLINE_IT out_it = blob->out_list (); + + ymin = (float) MAX_INT32; + ymax = (float) -MAX_INT32; + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + outline = out_it.data (); + pos = outline->start_pos (); //get coords + for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { + //inside + if (pos.x () >= leftx && pos.x () <= rightx) { + if (pos.y () > ymax) + ymax = pos.y (); + if (pos.y () < ymin) + ymin = pos.y (); + } + vec = outline->step (stepindex); + pos += vec; //move to next + } + } +} + + +/********************************************************************** + * find_cblob_hlimits + * + * Scan the outlines of the cblob to locate the x min and max + * between the given y limits. + **********************************************************************/ + +void find_cblob_hlimits( //get x limits + C_BLOB *blob, //blob to search + float bottomy, //y limits + float topy, + float &xmin, //output x limits + float &xmax) { + INT16 stepindex; //current point + ICOORD pos; //current coords + ICOORD vec; //rotated step + C_OUTLINE *outline; //current outline + //outlines + C_OUTLINE_IT out_it = blob->out_list (); + + xmin = (float) MAX_INT32; + xmax = (float) -MAX_INT32; + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + outline = out_it.data (); + pos = outline->start_pos (); //get coords + for (stepindex = 0; stepindex < outline->pathlength (); stepindex++) { + //inside + if (pos.y () >= bottomy && pos.y () <= topy) { + if (pos.x () > xmax) + xmax = pos.x (); + if (pos.x () < xmin) + xmin = pos.x (); + } + vec = outline->step (stepindex); + pos += vec; //move to next + } + } +} + + +/********************************************************************** + * rotate_blob + * + * Poly copy the blob and rotate the copy by the given vector. + **********************************************************************/ + +PBLOB *rotate_blob( //get y limits + PBLOB *blob, //blob to search + FCOORD rotation //vector to rotate by + ) { + PBLOB *copy; //copy of blob + POLYPT *polypt; //current point + OUTLINE_IT out_it; + POLYPT_IT poly_it; //outline pts + + copy = new PBLOB; + *copy = *blob; //deep copy + out_it.set_to_list (copy->out_list ()); + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + //get points + poly_it.set_to_list (out_it.data ()->polypts ()); + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); + poly_it.forward ()) { + polypt = poly_it.data (); + //rotate it + polypt->pos.rotate (rotation); + polypt->vec.rotate (rotation); + } + out_it.data ()->compute_bb (); + } + return copy; +} + + +/********************************************************************** + * rotate_cblob + * + * Poly copy the blob and rotate the copy by the given vector. + **********************************************************************/ + +PBLOB *rotate_cblob( //rotate it + C_BLOB *blob, //blob to search + float xheight, //for poly approx + FCOORD rotation //for landscape + ) { + PBLOB *copy; //copy of blob + POLYPT *polypt; //current point + OUTLINE_IT out_it; + POLYPT_IT poly_it; //outline pts + + copy = new PBLOB (blob, xheight); + out_it.set_to_list (copy->out_list ()); + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + //get points + poly_it.set_to_list (out_it.data ()->polypts ()); + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); + poly_it.forward ()) { + polypt = poly_it.data (); + //rotate it + polypt->pos.rotate (rotation); + polypt->vec.rotate (rotation); + } + out_it.data ()->compute_bb (); + } + return copy; +} + + +/********************************************************************** + * crotate_cblob + * + * Rotate the copy by the given vector and return a C_BLOB. + **********************************************************************/ + +C_BLOB *crotate_cblob( //rotate it + C_BLOB *blob, //blob to search + FCOORD rotation //for landscape + ) { + C_OUTLINE_LIST out_list; //output outlines + //input outlines + C_OUTLINE_IT in_it = blob->out_list (); + //output outlines + C_OUTLINE_IT out_it = &out_list; + + for (in_it.mark_cycle_pt (); !in_it.cycled_list (); in_it.forward ()) { + out_it.add_after_then_move (new C_OUTLINE (in_it.data (), rotation)); + } + return new C_BLOB (&out_list); +} + + +/********************************************************************** + * box_next + * + * Compute the bounding box of this blob with merging of x overlaps + * but no pre-chopping. + * Then move the iterator on to the start of the next blob. + **********************************************************************/ + +BOX box_next( //get bounding box + BLOBNBOX_IT *it //iterator to blobds + ) { + BLOBNBOX *blob; //current blob + BOX result; //total box + + blob = it->data (); + result = blob->bounding_box (); + do { + it->forward (); + blob = it->data (); + if (blob->blob () == NULL && blob->cblob () == NULL) + //was pre-chopped + result += blob->bounding_box (); + } + //until next real blob + while (blob->blob () == NULL && blob->cblob () == NULL || blob->joined_to_prev ()); + return result; +} + + +/********************************************************************** + * box_next_pre_chopped + * + * Compute the bounding box of this blob with merging of x overlaps + * but WITH pre-chopping. + * Then move the iterator on to the start of the next pre-chopped blob. + **********************************************************************/ + +BOX box_next_pre_chopped( //get bounding box + BLOBNBOX_IT *it //iterator to blobds + ) { + BLOBNBOX *blob; //current blob + BOX result; //total box + + blob = it->data (); + result = blob->bounding_box (); + do { + it->forward (); + blob = it->data (); + } + //until next real blob + while (blob->joined_to_prev ()); + return result; +} + + +/********************************************************************** + * TO_ROW::TO_ROW + * + * Constructor to make a row from a blob. + **********************************************************************/ + +TO_ROW::TO_ROW ( //constructor +BLOBNBOX * blob, //first blob +float top, //corrected top +float bottom, //of row +float row_size //ideal +):y_min (bottom), y_max (top), initial_y_min (bottom) { + float diff; //in size + BLOBNBOX_IT it = &blobs; //list of blobs + + it.add_to_end (blob); + diff = top - bottom - row_size; + if (diff > 0) { + y_max -= diff / 2; + y_min += diff / 2; + } + //very small object + else if ((top - bottom) * 3 < row_size) { + diff = row_size / 3 + bottom - top; + y_max += diff / 2; + y_min -= diff / 2; + } +} + + +/********************************************************************** + * TO_ROW:add_blob + * + * Add the blob to the end of the row. + **********************************************************************/ + +void TO_ROW::add_blob( //constructor + BLOBNBOX *blob, //first blob + float top, //corrected top + float bottom, //of row + float row_size //ideal + ) { + float allowed; //allowed expansion + float available; //expansion + BLOBNBOX_IT it = &blobs; //list of blobs + + it.add_to_end (blob); + allowed = row_size + y_min - y_max; + if (allowed > 0) { + available = top > y_max ? top - y_max : 0; + if (bottom < y_min) + //total available + available += y_min - bottom; + if (available > 0) { + available += available; //do it gradually + if (available < allowed) + available = allowed; + if (bottom < y_min) + y_min -= (y_min - bottom) * allowed / available; + if (top > y_max) + y_max += (top - y_max) * allowed / available; + } + } +} + + +/********************************************************************** + * TO_ROW:insert_blob + * + * Add the blob to the row in the correct position. + **********************************************************************/ + +void TO_ROW::insert_blob( //constructor + BLOBNBOX *blob //first blob + ) { + BLOBNBOX_IT it = &blobs; //list of blobs + + if (it.empty ()) + it.add_before_then_move (blob); + else { + it.mark_cycle_pt (); + while (!it.cycled_list () + && it.data ()->bounding_box ().left () <= + blob->bounding_box ().left ()) + it.forward (); + if (it.cycled_list ()) + it.add_to_end (blob); + else + it.add_before_stay_put (blob); + } +} + + +/********************************************************************** + * TO_ROW::compute_vertical_projection + * + * Compute the vertical projection of a TO_ROW from its blobs. + **********************************************************************/ + +void TO_ROW::compute_vertical_projection() { //project whole row + BOX row_box; //bound of row + BLOBNBOX *blob; //current blob + BOX blob_box; //bounding box + BLOBNBOX_IT blob_it = blob_list (); + + if (blob_it.empty ()) + return; + row_box = blob_it.data ()->bounding_box (); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) + row_box += blob_it.data ()->bounding_box (); + + projection.set_range (row_box.left () - PROJECTION_MARGIN, + row_box.right () + PROJECTION_MARGIN); + projection_left = row_box.left () - PROJECTION_MARGIN; + projection_right = row_box.right () + PROJECTION_MARGIN; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (blob->blob () != NULL) + vertical_blob_projection (blob->blob (), &projection); + else if (blob->cblob () != NULL) + vertical_cblob_projection (blob->cblob (), &projection); + } +} + + +/********************************************************************** + * vertical_blob_projection + * + * Compute the vertical projection of a blob from its outlines + * and add to the given STATS. + **********************************************************************/ + +void vertical_blob_projection( //project outlines + PBLOB *blob, //blob to project + STATS *stats //output + ) { + //outlines of blob + OUTLINE_IT out_it = blob->out_list (); + + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + vertical_outline_projection (out_it.data (), stats); + } +} + + +/********************************************************************** + * vertical_outline_projection + * + * Compute the vertical projection of a outline from its outlines + * and add to the given STATS. + **********************************************************************/ + +void vertical_outline_projection( //project outlines + OUTLINE *outline, //outline to project + STATS *stats //output + ) { + POLYPT *polypt; //current point + INT32 xcoord; //current pixel coord + float end_x; //end of vec + POLYPT_IT poly_it = outline->polypts (); + OUTLINE_IT out_it = outline->child (); + float ymean; //amount to add + float width; //amount of x + + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) { + polypt = poly_it.data (); + end_x = polypt->pos.x () + polypt->vec.x (); + if (polypt->vec.x () > 0) { + for (xcoord = (INT32) floor (polypt->pos.x ()); + xcoord < end_x; xcoord++) { + if (polypt->pos.x () < xcoord) { + width = (float) xcoord; + ymean = + polypt->vec.y () * (xcoord - + polypt->pos.x ()) / polypt->vec.x () + + polypt->pos.y (); + } + else { + width = polypt->pos.x (); + ymean = polypt->pos.y (); + } + if (end_x > xcoord + 1) { + width -= xcoord + 1; + ymean += + polypt->vec.y () * (xcoord + 1 - + polypt->pos.x ()) / polypt->vec.x () + + polypt->pos.y (); + } + else { + width -= end_x; + ymean += polypt->pos.y () + polypt->vec.y (); + } + ymean = ymean * width / 2; + stats->add (xcoord, (INT32) floor (ymean + 0.5)); + } + } + else if (polypt->vec.x () < 0) { + for (xcoord = (INT32) floor (end_x); + xcoord < polypt->pos.x (); xcoord++) { + if (polypt->pos.x () > xcoord + 1) { + width = xcoord + 1.0f; + ymean = + polypt->vec.y () * (xcoord + 1 - + polypt->pos.x ()) / polypt->vec.x () + + polypt->pos.y (); + } + else { + width = polypt->pos.x (); + ymean = polypt->pos.y (); + } + if (end_x < xcoord) { + width -= xcoord; + ymean += + polypt->vec.y () * (xcoord - + polypt->pos.x ()) / polypt->vec.x () + + polypt->pos.y (); + } + else { + width -= end_x; + ymean += polypt->pos.y () + polypt->vec.y (); + } + ymean = ymean * width / 2; + stats->add (xcoord, (INT32) floor (ymean + 0.5)); + } + } + } + + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + vertical_outline_projection (out_it.data (), stats); + } +} + + +/********************************************************************** + * vertical_cblob_projection + * + * Compute the vertical projection of a cblob from its outlines + * and add to the given STATS. + **********************************************************************/ + +void vertical_cblob_projection( //project outlines + C_BLOB *blob, //blob to project + STATS *stats //output + ) { + //outlines of blob + C_OUTLINE_IT out_it = blob->out_list (); + + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + vertical_coutline_projection (out_it.data (), stats); + } +} + + +/********************************************************************** + * vertical_coutline_projection + * + * Compute the vertical projection of a outline from its outlines + * and add to the given STATS. + **********************************************************************/ + +void vertical_coutline_projection( //project outlines + C_OUTLINE *outline, //outline to project + STATS *stats //output + ) { + ICOORD pos; //current point + ICOORD step; //edge step + INT32 length; //of outline + INT16 stepindex; //current step + C_OUTLINE_IT out_it = outline->child (); + + pos = outline->start_pos (); + length = outline->pathlength (); + for (stepindex = 0; stepindex < length; stepindex++) { + step = outline->step (stepindex); + if (step.x () > 0) { + if (pitsync_projection_fix) + stats->add (pos.x (), -pos.y ()); + else + stats->add (pos.x (), pos.y ()); + } + else if (step.x () < 0) { + if (pitsync_projection_fix) + stats->add (pos.x () - 1, pos.y ()); + else + stats->add (pos.x () - 1, -pos.y ()); + } + pos += step; + } + + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + vertical_coutline_projection (out_it.data (), stats); + } +} + + +/********************************************************************** + * TO_BLOCK::TO_BLOCK + * + * Constructor to make a TO_BLOCK from a real block. + **********************************************************************/ + +TO_BLOCK::TO_BLOCK( //make a block + BLOCK *src_block //real block + ) { + block = src_block; +} + +static void clear_blobnboxes(BLOBNBOX_LIST* boxes) { + BLOBNBOX_IT it = boxes; + // A BLOBNBOX generally doesn't own its blobs, so if they do, you + // have to delete them explicitly. + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + BLOBNBOX* box = it.data(); + if (box->blob() != NULL) + delete box->blob(); + if (box->cblob() != NULL) + delete box->cblob(); + } +} + +TO_BLOCK::~TO_BLOCK() { + // Any residual BLOBNBOXes at this stage own their blobs, so delete them. + clear_blobnboxes(&blobs); + clear_blobnboxes(&underlines); + clear_blobnboxes(&noise_blobs); + clear_blobnboxes(&small_blobs); + clear_blobnboxes(&large_blobs); +} + diff --git a/ccstruct/blobbox.h b/ccstruct/blobbox.h new file mode 100644 index 0000000000..7934c34edc --- /dev/null +++ b/ccstruct/blobbox.h @@ -0,0 +1,381 @@ +/********************************************************************** + * File: blobbox.h (Formerly blobnbox.h) + * Description: Code for the textord blob class. + * Author: Ray Smith + * Created: Thu Jul 30 09:08:51 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BLOBBOX_H +#define BLOBBOX_H + +#include "varable.h" +#include "clst.h" +#include "elst2.h" +#include "werd.h" +#include "ocrblock.h" +#include "statistc.h" + +extern double_VAR_H (textord_error_weight, 3, +"Weighting for error in believability"); + +enum PITCH_TYPE +{ + PITCH_DUNNO, //insufficient data + PITCH_DEF_FIXED, //definitely fixed + PITCH_MAYBE_FIXED, //could be + PITCH_DEF_PROP, + PITCH_MAYBE_PROP, + PITCH_CORR_FIXED, + PITCH_CORR_PROP +}; + +class BLOBNBOX; +ELISTIZEH (BLOBNBOX) +class BLOBNBOX:public ELIST_LINK +{ + public: + BLOBNBOX() { //empty + blob_ptr = NULL; + cblob_ptr = NULL; + joined = FALSE; + reduced = FALSE; + area = 0; + } + BLOBNBOX( //constructor + PBLOB *srcblob) { + blob_ptr = srcblob; + cblob_ptr = NULL; + box = srcblob->bounding_box (); + joined = FALSE; + reduced = FALSE; + area = (int) srcblob->area (); + } + BLOBNBOX( //constructor + C_BLOB *srcblob) { + blob_ptr = NULL; + cblob_ptr = srcblob; + box = srcblob->bounding_box (); + joined = FALSE; + reduced = FALSE; + area = (int) srcblob->area (); + } + + //get bounding box + const BOX &bounding_box() const { + return box; + } + //get bounding box + const BOX &reduced_box() const { + return red_box; + } + void set_reduced_box( //set other box + BOX new_box) { + red_box = new_box; + reduced = TRUE; + } + INT32 enclosed_area() const { //get area + return area; + } + + void rotate_box( //just box + FCOORD vec) { + box.rotate (vec); + } + + BOOL8 joined_to_prev() const { //access function + return joined != 0; + } + BOOL8 red_box_set() const { //access function + return reduced != 0; + } + void merge( //merge with next + BLOBNBOX *nextblob); + void chop( //fake chop blob + BLOBNBOX_IT *start_it, //location of this + BLOBNBOX_IT *blob_it, //iterator + FCOORD rotation, //for landscape + float xheight); //line height + + PBLOB *blob() { //access function + return blob_ptr; + } + C_BLOB *cblob() { //access function + return cblob_ptr; + } + +#ifndef GRAPHICS_DISABLED + void plot( //draw one + WINDOW window, //window to draw in + COLOUR blob_colour, //for outer bits + COLOUR child_colour) { //for holes + if (blob_ptr != NULL) + blob_ptr->plot (window, blob_colour, child_colour); + if (cblob_ptr != NULL) + cblob_ptr->plot (window, blob_colour, child_colour); + } +#endif + + NEWDELETE2 (BLOBNBOX) private: + int area:30; //enclosed area + int joined:1; //joined to prev + int reduced:1; //reduced box set + BOX box; //bounding box + BOX red_box; //bounding box + PBLOB *blob_ptr; //poly blob + C_BLOB *cblob_ptr; //edgestep blob +}; + +class TO_ROW:public ELIST2_LINK +{ + public: + TO_ROW() { + } //empty + TO_ROW( //constructor + BLOBNBOX *blob, //from first blob + float top, //of row //target height + float bottom, + float row_size); + + float max_y() const { //access function + return y_max; + } + float min_y() const { + return y_min; + } + float mean_y() const { + return (y_min + y_max) / 2.0f; + } + float initial_min_y() const { + return initial_y_min; + } + float line_m() const { //access to line fit + return m; + } + float line_c() const { + return c; + } + float line_error() const { + return error; + } + float parallel_c() const { + return para_c; + } + float parallel_error() const { + return para_error; + } + float believability() const { //baseline goodness + return credibility; + } + float intercept() const { //real parallel_c + return y_origin; + } + void add_blob( //put in row + BLOBNBOX *blob, //blob to add + float top, //of row //target height + float bottom, + float row_size); + void insert_blob( //put in row in order + BLOBNBOX *blob); + + BLOBNBOX_LIST *blob_list() { //get list + return &blobs; + } + + void set_line( //set line spec + float new_m, //line to set + float new_c, + float new_error) { + m = new_m; + c = new_c; + error = new_error; + } + void set_parallel_line( //set fixed gradient line + float gradient, //page gradient + float new_c, + float new_error) { + para_c = new_c; + para_error = new_error; + credibility = + (float) (blobs.length () - textord_error_weight * new_error); + y_origin = (float) (new_c / sqrt (1 + gradient * gradient)); + //real intercept + } + void set_limits( //set min,max + float new_min, //bottom and + float new_max) { //top of row + y_min = new_min; + y_max = new_max; + } + void compute_vertical_projection(); + //get projection + + //true when dead + NEWDELETE2 (TO_ROW) BOOL8 merged; + BOOL8 all_caps; //had no ascenders + BOOL8 used_dm_model; //in guessing pitch + INT16 projection_left; //start of projection + INT16 projection_right; //start of projection + PITCH_TYPE pitch_decision; //how strong is decision + float fixed_pitch; //pitch or 0 + float fp_space; //sp if fixed pitch + float fp_nonsp; //nonsp if fixed pitch + float pr_space; //sp if prop + float pr_nonsp; //non sp if prop + float spacing; //to "next" row + float xheight; //of line + float ascrise; //ascenders + float descdrop; //descenders + INT32 min_space; //min size for real space + INT32 max_nonspace; //max size of non-space + INT32 space_threshold; //space vs nonspace + float kern_size; //average non-space + float space_size; //average space + WERD_LIST rep_words; //repeated chars + ICOORDELT_LIST char_cells; //fixed pitch cells + QSPLINE baseline; //curved baseline + STATS projection; //vertical projection + + private: + BLOBNBOX_LIST blobs; //blobs in row + float y_min; //coords + float y_max; + float initial_y_min; + float m, c; //line spec + float error; //line error + float para_c; //constrained fit + float para_error; + float y_origin; //rotated para_c; + float credibility; //baseline believability +}; + +ELIST2IZEH (TO_ROW) +class TO_BLOCK:public ELIST_LINK +{ + public: + TO_BLOCK() { + } //empty + TO_BLOCK( //constructor + BLOCK *src_block); //real block + ~TO_BLOCK(); + + TO_ROW_LIST *get_rows() { //access function + return &row_list; + } + + void print_rows() { //debug info + TO_ROW_IT row_it = &row_list; + TO_ROW *row; + + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); + row_it.forward ()) { + row = row_it.data (); + printf ("Row range (%g,%g), para_c=%g, blobcount=" INT32FORMAT + "\n", row->min_y (), row->max_y (), row->parallel_c (), + row->blob_list ()->length ()); + } + } + + BLOBNBOX_LIST blobs; //medium size + BLOBNBOX_LIST underlines; //underline blobs + BLOBNBOX_LIST noise_blobs; //very small + BLOBNBOX_LIST small_blobs; //fairly small + BLOBNBOX_LIST large_blobs; //big blobs + BLOCK *block; //real block + PITCH_TYPE pitch_decision; //how strong is decision + float line_spacing; //estimate + float line_size; //estimate + float max_blob_size; //line assignment limit + float baseline_offset; //phase shift + float xheight; //median blob size + float fixed_pitch; //pitch or 0 + float kern_size; //average non-space + float space_size; //average space + INT32 min_space; //min definite space + INT32 max_nonspace; //max definite + float fp_space; //sp if fixed pitch + float fp_nonsp; //nonsp if fixed pitch + float pr_space; //sp if prop + float pr_nonsp; //non sp if prop + TO_ROW *key_row; //starting row + + NEWDELETE2 (TO_BLOCK) private: + TO_ROW_LIST row_list; //temporary rows +}; + +ELISTIZEH (TO_BLOCK) +extern double_VAR_H (textord_error_weight, 3, +"Weighting for error in believability"); +void find_blob_limits( //get y limits + PBLOB *blob, //blob to search + float leftx, //x limits + float rightx, + FCOORD rotation, //for landscape + float &ymin, //output y limits + float &ymax); +void find_cblob_limits( //get y limits + C_BLOB *blob, //blob to search + float leftx, //x limits + float rightx, + FCOORD rotation, //for landscape + float &ymin, //output y limits + float &ymax); +void find_cblob_vlimits( //get y limits + C_BLOB *blob, //blob to search + float leftx, //x limits + float rightx, + float &ymin, //output y limits + float &ymax); +void find_cblob_hlimits( //get x limits + C_BLOB *blob, //blob to search + float bottomy, //y limits + float topy, + float &xmin, //output x limits + float &xymax); +PBLOB *rotate_blob( //get y limits + PBLOB *blob, //blob to search + FCOORD rotation //vector to rotate by + ); +PBLOB *rotate_cblob( //rotate it + C_BLOB *blob, //blob to search + float xheight, //for poly approx + FCOORD rotation //for landscape + ); +C_BLOB *crotate_cblob( //rotate it + C_BLOB *blob, //blob to search + FCOORD rotation //for landscape + ); +BOX box_next( //get bounding box + BLOBNBOX_IT *it //iterator to blobds + ); +BOX box_next_pre_chopped( //get bounding box + BLOBNBOX_IT *it //iterator to blobds + ); +void vertical_blob_projection( //project outlines + PBLOB *blob, //blob to project + STATS *stats //output + ); + //project outlines +void vertical_outline_projection(OUTLINE *outline, //outline to project + STATS *stats //output + ); +void vertical_cblob_projection( //project outlines + C_BLOB *blob, //blob to project + STATS *stats //output + ); +void vertical_coutline_projection( //project outlines + C_OUTLINE *outline, //outline to project + STATS *stats //output + ); +#endif diff --git a/ccstruct/blobs.cpp b/ccstruct/blobs.cpp new file mode 100644 index 0000000000..73fd09e11b --- /dev/null +++ b/ccstruct/blobs.cpp @@ -0,0 +1,247 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: blobs.c (Formerly blobs.c) + * Description: Blob definition + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 27 15:39:52 1989 + * Modified: Thu Mar 28 15:33:26 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "mfcpch.h" +#include "blobs.h" +#include "cutil.h" +#include "emalloc.h" +#include "structures.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * blob_origin + * + * Compute the origin of a compound blob, define to be the centre + * of the bounding box. + **********************************************************************/ +void blob_origin(TBLOB *blob, /*blob to compute on */ + TPOINT *origin) { /*return value */ + TPOINT topleft; /*bounding box */ + TPOINT botright; + + /*find bounding box */ + blob_bounding_box(blob, &topleft, &botright); + /*centre of box */ + origin->x = (topleft.x + botright.x) / 2; + origin->y = (topleft.y + botright.y) / 2; +} + + +/********************************************************************** + * blob_bounding_box + * + * Compute the bounding_box of a compound blob, define to be the + * max coordinate value of the bounding boxes of all the top-level + * outlines in the box. + **********************************************************************/ +void blob_bounding_box(TBLOB *blob, /*blob to compute on */ + register TPOINT *topleft, /*bounding box */ + register TPOINT *botright) { + register TESSLINE *outline; /*current outline */ + + if (blob == NULL || blob->outlines == NULL) { + topleft->x = topleft->y = 0; + *botright = *topleft; /*default value */ + } + else { + outline = blob->outlines; + *topleft = outline->topleft; + *botright = outline->botright; + for (outline = outline->next; outline != NULL; outline = outline->next) { + if (outline->topleft.x < topleft->x) + /*find extremes */ + topleft->x = outline->topleft.x; + if (outline->botright.x > botright->x) + /*find extremes */ + botright->x = outline->botright.x; + if (outline->topleft.y > topleft->y) + /*find extremes */ + topleft->y = outline->topleft.y; + if (outline->botright.y < botright->y) + /*find extremes */ + botright->y = outline->botright.y; + } + } +} + + +/********************************************************************** + * blobs_bounding_box + * + * Return the smallest extreme point that contain this word. + **********************************************************************/ +void blobs_bounding_box(TBLOB *blobs, TPOINT *topleft, TPOINT *botright) { + TPOINT tl; + TPOINT br; + TBLOB *blob; + /* Start with first blob */ + blob_bounding_box(blobs, topleft, botright); + + iterate_blobs(blob, blobs) { + blob_bounding_box(blob, &tl, &br); + + if (tl.x < topleft->x) + topleft->x = tl.x; + if (tl.y > topleft->y) + topleft->y = tl.y; + if (br.x > botright->x) + botright->x = br.x; + if (br.y < botright->y) + botright->y = br.y; + } +} + + +/********************************************************************** + * blobs_origin + * + * Compute the origin of a compound blob, define to be the centre + * of the bounding box. + **********************************************************************/ +void blobs_origin(TBLOB *blobs, /*blob to compute on */ + TPOINT *origin) { /*return value */ + TPOINT topleft; /*bounding box */ + TPOINT botright; + + /*find bounding box */ + blobs_bounding_box(blobs, &topleft, &botright); + /*center of box */ + origin->x = (topleft.x + botright.x) / 2; + origin->y = (topleft.y + botright.y) / 2; +} + + +/********************************************************************** + * blobs_widths + * + * Compute the widths of a list of blobs. Return an array of the widths + * and gaps. + **********************************************************************/ +WIDTH_RECORD *blobs_widths(TBLOB *blobs) { /*blob to compute on */ + WIDTH_RECORD *width_record; + TPOINT topleft; /*bounding box */ + TPOINT botright; + TBLOB *blob; /*blob to compute on */ + int i = 0; + int blob_end; + int num_blobs = count_blobs (blobs); + + /* Get memory */ + width_record = (WIDTH_RECORD *) memalloc (sizeof (int) * num_blobs * 2); + width_record->num_chars = num_blobs; + + blob_bounding_box(blobs, &topleft, &botright); + width_record->widths[i++] = botright.x - topleft.x; + /* First width */ + blob_end = botright.x; + + iterate_blobs (blob, blobs->next) { + blob_bounding_box(blob, &topleft, &botright); + width_record->widths[i++] = topleft.x - blob_end; + width_record->widths[i++] = botright.x - topleft.x; + blob_end = botright.x; + } + return (width_record); +} + + +/********************************************************************** + * count_blobs + * + * Return a count of the number of blobs attached to this one. + **********************************************************************/ +int count_blobs(TBLOB *blobs) { + TBLOB *b; + int x = 0; + + iterate_blobs (b, blobs) x++; + return (x); +} + + +/********************************************************************** + * delete_word + * + * Reclaim the memory taken by this word structure and all of its + * lower level structures. + **********************************************************************/ +void delete_word(TWERD *word) { + TBLOB *blob; + TBLOB *nextblob; + TESSLINE *outline; + TESSLINE *nextoutline; + TESSLINE *child; + TESSLINE *nextchild; + + for (blob = word->blobs; blob; blob = nextblob) { + nextblob = blob->next; + + for (outline = blob->outlines; outline; outline = nextoutline) { + nextoutline = outline->next; + + delete_edgepts (outline->loop); + + for (child = outline->child; child; child = nextchild) { + nextchild = child->next; + + delete_edgepts (child->loop); + + oldoutline(child); + } + oldoutline(outline); + } + oldblob(blob); + } + if (word->correct != NULL) + strfree (word->correct); /* Reclaim memory */ + oldword(word); +} + + +/********************************************************************** + * delete_edgepts + * + * Delete a list of EDGEPT structures. + **********************************************************************/ +void delete_edgepts(register EDGEPT *edgepts) { + register EDGEPT *this_edge; + register EDGEPT *next_edge; + + if (edgepts == NULL) + return; + + this_edge = edgepts; + do { + next_edge = this_edge->next; + oldedgept(this_edge); + this_edge = next_edge; + } + while (this_edge != edgepts); +} diff --git a/ccstruct/blobs.h b/ccstruct/blobs.h new file mode 100644 index 0000000000..16c64b423a --- /dev/null +++ b/ccstruct/blobs.h @@ -0,0 +1,119 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: blobs.h (Formerly blobs.h) + * Description: Blob definition + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 27 15:39:52 1989 + * Modified: Thu Mar 28 15:33:38 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ + +#ifndef BLOBS_H +#define BLOBS_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "vecfuncs.h" +#include "tessclas.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef struct +{ /* Widths of pieces */ + int num_chars; + int widths[1]; +} WIDTH_RECORD; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * free_widths + * + * Free the memory taken up by a width array. + **********************************************************************/ +#define free_widths(w) \ +if (w) memfree (w) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void blob_origin(TBLOB *blob, /*blob to compute on */ + TPOINT *origin); /*return value */ + + /*blob to compute on */ +void blob_bounding_box(TBLOB *blob, + register TPOINT *topleft, /*bounding box */ + register TPOINT *botright); + +void blobs_bounding_box(TBLOB *blobs, TPOINT *topleft, TPOINT *botright); + +void blobs_origin(TBLOB *blobs, /*blob to compute on */ + TPOINT *origin); /*return value */ + + /*blob to compute on */ +WIDTH_RECORD *blobs_widths(TBLOB *blobs); + +int count_blobs(TBLOB *blobs); + +void delete_word(TWERD *word); + +void delete_edgepts(register EDGEPT *edgepts); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* blobs.c +void blob_origin + _ARGS((BLOB *blob, + TPOINT *origin)); + +void blob_bounding_box + _ARGS((BLOB *blob, + TPOINT *topleft, + TPOINT *botright)); + +void blobs_bounding_box + _ARGS((BLOB *blobs, + TPOINT *topleft, + TPOINT *botright)); + +void blobs_origin + _ARGS((BLOB *blobs, + TPOINT *origin)); + +WIDTH_RECORD *blobs_widths + _ARGS((BLOB *blobs)); + +int count_blobs + _ARGS((BLOB *blobs)); + +void delete_word + _ARGS((TWERD *word)); + +void delete_edgepts + _ARGS((EDGEPT *edgepts)); +#undef _ARGS +*/ +#endif diff --git a/ccstruct/blread.cpp b/ccstruct/blread.cpp new file mode 100644 index 0000000000..93a3412a12 --- /dev/null +++ b/ccstruct/blread.cpp @@ -0,0 +1,537 @@ +/********************************************************************** + * File: blread.cpp (Formerly pdread.c) + * Description: Friend function of BLOCK to read the uscan pd file. + * Author: Ray Smith + * Created: Mon Mar 18 14:39:00 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#ifdef __UNIX__ +#include +#endif +#include "scanutils.h" +#include "fileerr.h" +#include "imgtiff.h" +#include "pdclass.h" +#include "rwpoly.h" +#include "blread.h" + +#define PD_EXT ".pd" +#define VEC_EXT ".vec" //accupage file +#define HPD_EXT ".bl" //hand pd file + //unlv zone file +#define UNLV_EXT ".uzn" +#define BLOCK_EXPANSION 8 //boundary expansion +#define EXTERN + +EXTERN BOOL_EVAR (ignore_weird_blocks, TRUE, "Don't read weird blocks"); + +static BOX convert_vec_block( //make non-rect block + VEC_ENTRY *entries, //vectors + UINT16 entry_count, //no of entries + INT32 ysize, //image size + ICOORDELT_IT *left_it, //block sides + ICOORDELT_IT *right_it); + +/********************************************************************** + * BLOCK::read_pd_file + * + * Read a whole pd file to make a list of blocks, or use the whole page. + **********************************************************************/ + +BOOL8 read_pd_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ) { + FILE *pdfp; //file pointer + BLOCK *block; //current block + INT32 block_count; //no of blocks + INT32 junk_count; //no of junks to read + INT32 junks[4]; //junk elements + INT32 vertex_count; //boundary vertices + INT32 xcoord; //current coords + INT32 ycoord; + INT32 prevx; //previous coords + INT32 prevy; + BLOCK_IT block_it = blocks; //block iterator + ICOORDELT_LIST dummy; //for constructor + ICOORDELT_IT left_it = &dummy; //iterator + ICOORDELT_IT right_it = &dummy;//iterator + + if (read_hpd_file (name, xsize, ysize, blocks)) + return TRUE; //succeeded + if (read_vec_file (name, xsize, ysize, blocks)) + return TRUE; //succeeded + if (read_unlv_file (name, xsize, ysize, blocks)) + return TRUE; //succeeded + name += PD_EXT; //add extension + if ((pdfp = fopen (name.string (), "r")) == NULL) { + //make rect block + block = new BLOCK (name.string (), TRUE, 0, 0, 0, 0, xsize, ysize); + block_it.add_to_end (block); //on end of list + return FALSE; //didn't read one + } + else { + if (fread (&block_count, sizeof (block_count), 1, pdfp) != 1) + READFAILED.error ("read_pd_file", EXIT, "Block count"); + tprintf ("%d blocks in .pd file.\n", block_count); + while (block_count > 0) { + if (fread (&junk_count, sizeof (junk_count), 1, pdfp) != 1) + READFAILED.error ("read_pd_file", EXIT, "Junk count"); + if (fread (&vertex_count, sizeof (vertex_count), 1, pdfp) != 1) + READFAILED.error ("read_pd_file", EXIT, "Vertex count"); + block = new BLOCK; //make a block + //on end of list + block_it.add_to_end (block); + left_it.set_to_list (&block->leftside); + right_it.set_to_list (&block->rightside); + + //read a pair + get_pd_vertex (pdfp, xsize, ysize, &block->box, xcoord, ycoord); + vertex_count -= 2; //count read ones + prevx = xcoord; + do { + if (xcoord == prevx) { + if (!right_it.empty ()) { + if (right_it.data ()->x () <= xcoord + BLOCK_EXPANSION) + right_it.data ()->set_y (right_it.data ()->y () + + BLOCK_EXPANSION); + else + right_it.data ()->set_y (right_it.data ()->y () - + BLOCK_EXPANSION); + } + right_it. + add_before_then_move (new + ICOORDELT (xcoord + BLOCK_EXPANSION, + ycoord)); + } + prevx = xcoord; //remember previous + prevy = ycoord; + get_pd_vertex (pdfp, xsize, ysize, &block->box, xcoord, ycoord); + vertex_count -= 2; //count read ones + } + while (ycoord <= prevy); + right_it.data ()->set_y (right_it.data ()->y () - BLOCK_EXPANSION); + + //start of left + left_it.add_to_end (new ICOORDELT (prevx - BLOCK_EXPANSION, prevy - BLOCK_EXPANSION)); + + do { + prevx = xcoord; //remember previous + get_pd_vertex (pdfp, xsize, ysize, &block->box, xcoord, ycoord); + vertex_count -= 2; + if (xcoord != prevx && vertex_count > 0) { + if (xcoord > prevx) + left_it. + add_to_end (new + ICOORDELT (xcoord - BLOCK_EXPANSION, + ycoord + BLOCK_EXPANSION)); + else + left_it. + add_to_end (new + ICOORDELT (xcoord - BLOCK_EXPANSION, + ycoord - BLOCK_EXPANSION)); + } + else if (vertex_count == 0) + left_it.add_to_end (new ICOORDELT (prevx - BLOCK_EXPANSION, + ycoord + BLOCK_EXPANSION)); + } + while (vertex_count > 0); //until all read + + while (junk_count > 0) { + if (fread (junks, sizeof (INT32), 4, pdfp) != 4) + READFAILED.error ("read_pd_file", EXIT, "Junk coords"); + junk_count--; + } + block_count--; //count read blocks + } + } + fclose(pdfp); + return TRUE; //read one +} + + +/********************************************************************** + * get_pd_vertex + * + * Read a pair of coords, invert the y and clip to image limits. + * Also update the bounding box. + * + * Read a whole pd file to make a list of blocks, or use the whole page. + **********************************************************************/ + +void get_pd_vertex( //get new vertex + FILE *pdfp, //file to read + INT32 xsize, //image size + INT32 ysize, //image size + BOX *box, //bounding box + INT32 &xcoord, //output coords + INT32 &ycoord) { + BOX new_coord; //expansion box + + //get new coords + if (fread (&xcoord, sizeof (xcoord), 1, pdfp) != 1) + READFAILED.error ("read_pd_file", EXIT, "Xcoord"); + if (fread (&ycoord, sizeof (ycoord), 1, pdfp) != 1) + READFAILED.error ("read_pd_file", EXIT, "Xcoord"); + ycoord = ysize - ycoord; //invert y + if (xcoord < BLOCK_EXPANSION) + xcoord = BLOCK_EXPANSION; //clip to limits + if (xcoord > xsize - BLOCK_EXPANSION) + xcoord = xsize - BLOCK_EXPANSION; + if (ycoord < BLOCK_EXPANSION) + ycoord = BLOCK_EXPANSION; + if (ycoord > ysize - BLOCK_EXPANSION) + ycoord = ysize - BLOCK_EXPANSION; + + new_coord = + BOX (ICOORD (xcoord - BLOCK_EXPANSION, ycoord - BLOCK_EXPANSION), + ICOORD (xcoord + BLOCK_EXPANSION, ycoord + BLOCK_EXPANSION)); + (*box) += new_coord; +} + + +/********************************************************************** + * BLOCK::read_hpd_file + * + * Read a whole hpd file to make a list of blocks. + * Return FALSE if the .vec fiel cannot be found + **********************************************************************/ + +BOOL8 read_hpd_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ) { + FILE *pdfp; //file pointer + PAGE_BLOCK_LIST *page_blocks; + INT32 block_no; //no of blocks + BLOCK_IT block_it = blocks; //block iterator + + name += HPD_EXT; //add extension + if ((pdfp = fopen (name.string (), "r")) == NULL) { + return FALSE; //can't find it + } + fclose(pdfp); + page_blocks = read_poly_blocks (name.string ()); + block_no = 0; + scan_hpd_blocks (name.string (), page_blocks, block_no, &block_it); + tprintf ("Text region count=%d\n", block_no); + return TRUE; //read one +} + + +/********************************************************************** + * BLOCK::scan_hpd_blocks + * + * Read a whole hpd file to make a list of blocks. + * Return FALSE if the .vec fiel cannot be found + **********************************************************************/ + +void scan_hpd_blocks( //print list of sides + const char *name, //block label + PAGE_BLOCK_LIST *page_blocks, //head of full pag + INT32 &block_no, //no of blocks + BLOCK_IT *block_it //block iterator + ) { + BLOCK *block; //current block + //page blocks + PAGE_BLOCK_IT pb_it = page_blocks; + PAGE_BLOCK *current_block; + TEXT_REGION_IT tr_it; + TEXT_BLOCK *tb; + TEXT_REGION *tr; + BOX *block_box; //from text region + + for (pb_it.mark_cycle_pt (); !pb_it.cycled_list (); pb_it.forward ()) { + current_block = pb_it.data (); + if (current_block->type () == PB_TEXT) { + tb = (TEXT_BLOCK *) current_block; + if (!tb->regions ()->empty ()) { + tr_it.set_to_list (tb->regions ()); + for (tr_it.mark_cycle_pt (); + !tr_it.cycled_list (); tr_it.forward ()) { + block_no++; + tr = tr_it.data (); + block_box = tr->bounding_box (); + block = new BLOCK (name, TRUE, 0, 0, + block_box->left (), block_box->bottom (), + block_box->right (), block_box->top ()); + block->hand_block = tr; + block->hand_poly = tr; + block_it->add_after_then_move (block); + } + } + } + else if (current_block->type () == PB_WEIRD + && !ignore_weird_blocks + && ((WEIRD_BLOCK *) current_block)->id_no () > 0) { + block_no++; + block_box = current_block->bounding_box (); + block = new BLOCK (name, TRUE, 0, 0, + block_box->left (), block_box->bottom (), + block_box->right (), block_box->top ()); + block->hand_block = NULL; + block->hand_poly = current_block; + block_it->add_after_then_move (block); + } + if (!current_block->child ()->empty ()) + scan_hpd_blocks (name, current_block->child (), block_no, block_it); + } +} + + + + + +/********************************************************************** + * BLOCK::read_vec_file + * + * Read a whole vec file to make a list of blocks. + * Return FALSE if the .vec fiel cannot be found + **********************************************************************/ + +BOOL8 read_vec_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ) { + FILE *pdfp; //file pointer + BLOCK *block; //current block + INT32 block_no; //no of blocks + INT32 block_index; //current blocks + INT32 vector_count; //total vectors + VEC_HEADER header; //file header + BLOCK_HEADER *vec_blocks; //blocks from file + VEC_ENTRY *vec_entries; //vectors from file + BLOCK_IT block_it = blocks; //block iterator + ICOORDELT_IT left_it; //iterators + ICOORDELT_IT right_it; + + name += VEC_EXT; //add extension + if ((pdfp = fopen (name.string (), "r")) == NULL) { + return FALSE; //can't find it + } + if (fread (&header, sizeof (header), 1, pdfp) != 1) + READFAILED.error ("read_vec_file", EXIT, "Header"); + //from intel + header.filesize = reverse32 (header.filesize); + header.bytesize = reverse16 (header.bytesize); + header.arraysize = reverse16 (header.arraysize); + header.width = reverse16 (header.width); + header.height = reverse16 (header.height); + header.res = reverse16 (header.res); + header.bpp = reverse16 (header.bpp); + tprintf ("%d blocks in %s file:", header.arraysize, VEC_EXT); + vector_count = header.filesize - header.arraysize * sizeof (BLOCK_HEADER); + vector_count /= sizeof (VEC_ENTRY); + vec_blocks = + (BLOCK_HEADER *) alloc_mem (header.arraysize * sizeof (BLOCK_HEADER)); + vec_entries = (VEC_ENTRY *) alloc_mem (vector_count * sizeof (VEC_ENTRY)); + xsize = header.width; //real image size + ysize = header.height; + if (fread (vec_blocks, sizeof (BLOCK_HEADER), header.arraysize, pdfp) + != static_cast(header.arraysize)) + READFAILED.error ("read_vec_file", EXIT, "Blocks"); + if (fread (vec_entries, sizeof (VEC_ENTRY), vector_count, pdfp) + != static_cast(vector_count)) + READFAILED.error ("read_vec_file", EXIT, "Vectors"); + for (block_index = 0; block_index < header.arraysize; block_index++) { + vec_blocks[block_index].offset = + reverse16 (vec_blocks[block_index].offset); + vec_blocks[block_index].order = + reverse16 (vec_blocks[block_index].order); + vec_blocks[block_index].entries = + reverse16 (vec_blocks[block_index].entries); + vec_blocks[block_index].charsize = + reverse16 (vec_blocks[block_index].charsize); + } + for (block_index = 0; block_index < vector_count; block_index++) { + vec_entries[block_index].start = + ICOORD (reverse16 (vec_entries[block_index].start.x ()), + reverse16 (vec_entries[block_index].start.y ())); + vec_entries[block_index].end = + ICOORD (reverse16 (vec_entries[block_index].end.x ()), + reverse16 (vec_entries[block_index].end.y ())); + } + for (block_no = 1; block_no <= header.arraysize; block_no++) { + for (block_index = 0; block_index < header.arraysize; block_index++) { + if (vec_blocks[block_index].order == block_no + && vec_blocks[block_index].valid) { + block = new BLOCK; + left_it.set_to_list (&block->leftside); + right_it.set_to_list (&block->rightside); + block->box = + convert_vec_block (&vec_entries + [vec_blocks[block_index].offset], + vec_blocks[block_index].entries, ysize, + &left_it, &right_it); + block->set_xheight (vec_blocks[block_index].charsize); + //on end of list + block_it.add_to_end (block); + // tprintf("Block at (%d,%d)->(%d,%d) has index %d and order %d\n", + // block->box.left(), + // block->box.bottom(), + // block->box.right(), + // block->box.top(), + // block_index,vec_blocks[block_index].order); + } + } + } + free_mem(vec_blocks); + free_mem(vec_entries); + tprintf ("%d valid\n", block_it.length ()); + fclose(pdfp); + return TRUE; //read one +} + + +/********************************************************************** + * BLOCK::convert_vec_block + * + * Read a whole vec file to make a list of blocks. + * Return FALSE if the .vec fiel cannot be found + **********************************************************************/ + +static BOX convert_vec_block( //make non-rect block + VEC_ENTRY *entries, //vectors + UINT16 entry_count, //no of entries + INT32 ysize, //image size + ICOORDELT_IT *left_it, //block sides + ICOORDELT_IT *right_it) { + BOX block_box; //bounding box + BOX vec_box; //box of vec + ICOORD box_point; //expanded coord + ICOORD shift_vec; //for box expansion + ICOORD prev_pt; //previous coord + ICOORD end_pt; //end of vector + INT32 vertex_index; //boundary vertices + + for (vertex_index = 0; vertex_index < entry_count; vertex_index++) { + entries[vertex_index].start = ICOORD (entries[vertex_index].start.x (), + ysize - 1 - + entries[vertex_index].start.y ()); + entries[vertex_index].end = + ICOORD (entries[vertex_index].end.x (), + ysize - 1 - entries[vertex_index].end.y ()); + vec_box = BOX (entries[vertex_index].start, entries[vertex_index].end); + block_box += vec_box; //find total bounds + } + + for (vertex_index = 0; vertex_index < entry_count + && (entries[vertex_index].start.y () != block_box.bottom () + || entries[vertex_index].end.y () != block_box.bottom ()); + vertex_index++); + ASSERT_HOST (vertex_index < entry_count); + prev_pt = entries[vertex_index].start; + end_pt = entries[vertex_index].end; + do { + for (vertex_index = 0; vertex_index < entry_count + && entries[vertex_index].start != end_pt; vertex_index++); + //found start of vertical + ASSERT_HOST (vertex_index < entry_count); + box_point = entries[vertex_index].start; + if (box_point.x () <= prev_pt.x ()) + shift_vec = ICOORD (-BLOCK_EXPANSION, -BLOCK_EXPANSION); + else + shift_vec = ICOORD (-BLOCK_EXPANSION, BLOCK_EXPANSION); + left_it->add_to_end (new ICOORDELT (box_point + shift_vec)); + prev_pt = box_point; + for (vertex_index = 0; vertex_index < entry_count + && entries[vertex_index].start != end_pt; vertex_index++); + //found horizontal + ASSERT_HOST (vertex_index < entry_count); + end_pt = entries[vertex_index].end; + } + while (end_pt.y () < block_box.top ()); + shift_vec = ICOORD (-BLOCK_EXPANSION, BLOCK_EXPANSION); + left_it->add_to_end (new ICOORDELT (end_pt + shift_vec)); + + for (vertex_index = 0; vertex_index < entry_count + && (entries[vertex_index].start.y () != block_box.top () + || entries[vertex_index].end.y () != block_box.top ()); + vertex_index++); + ASSERT_HOST (vertex_index < entry_count); + prev_pt = entries[vertex_index].start; + end_pt = entries[vertex_index].end; + do { + for (vertex_index = 0; vertex_index < entry_count + && entries[vertex_index].start != end_pt; vertex_index++); + //found start of vertical + ASSERT_HOST (vertex_index < entry_count); + box_point = entries[vertex_index].start; + if (box_point.x () < prev_pt.x ()) + shift_vec = ICOORD (BLOCK_EXPANSION, -BLOCK_EXPANSION); + else + shift_vec = ICOORD (BLOCK_EXPANSION, BLOCK_EXPANSION); + right_it->add_before_then_move (new ICOORDELT (box_point + shift_vec)); + prev_pt = box_point; + for (vertex_index = 0; vertex_index < entry_count + && entries[vertex_index].start != end_pt; vertex_index++); + //found horizontal + ASSERT_HOST (vertex_index < entry_count); + end_pt = entries[vertex_index].end; + } + while (end_pt.y () > block_box.bottom ()); + shift_vec = ICOORD (BLOCK_EXPANSION, -BLOCK_EXPANSION); + right_it->add_before_then_move (new ICOORDELT (end_pt + shift_vec)); + + shift_vec = ICOORD (BLOCK_EXPANSION, BLOCK_EXPANSION); + box_point = block_box.botleft () - shift_vec; + end_pt = block_box.topright () + shift_vec; + return BOX (box_point, end_pt); +} + + +/********************************************************************** + * read_unlv_file + * + * Read a whole unlv zone file to make a list of blocks. + **********************************************************************/ + +BOOL8 read_unlv_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ) { + FILE *pdfp; //file pointer + BLOCK *block; //current block + int x; //current top-down coords + int y; + int width; //of current block + int height; + BLOCK_IT block_it = blocks; //block iterator + + name += UNLV_EXT; //add extension + if ((pdfp = fopen (name.string (), "r")) == NULL) { + return FALSE; //didn't read one + } + else { + while (fscanf (pdfp, "%d %d %d %d %*s", &x, &y, &width, &height) >= 4) { + //make rect block + block = new BLOCK (name.string (), TRUE, 0, 0, (INT16) x, (INT16) (ysize - 1 - y - height), (INT16) (x + width), (INT16) (ysize - 1 - y)); + //on end of list + block_it.add_to_end (block); + } + fclose(pdfp); + } + return true; +} diff --git a/ccstruct/blread.h b/ccstruct/blread.h new file mode 100644 index 0000000000..bbd03239cc --- /dev/null +++ b/ccstruct/blread.h @@ -0,0 +1,63 @@ +/********************************************************************** + * File: blread.h (Formerly pdread.h) + * Description: Friend function of BLOCK to read the uscan pd file. + * Author: Ray Smith + * Created: Mon Mar 18 14:39:00 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BLREAD_H +#define BLREAD_H + +#include "varable.h" +#include "ocrblock.h" + +BOOL8 read_pd_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ); +void get_pd_vertex( //get new vertex + FILE *pdfp, //file to read + INT32 xsize, //image size + INT32 ysize, //image size + BOX *box, //bounding box + INT32 &xcoord, //output coords + INT32 &ycoord); +BOOL8 read_hpd_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ); +void scan_hpd_blocks( //print list of sides + const char *name, //block label + PAGE_BLOCK_LIST *page_blocks, //head of full pag + INT32 &block_no, //no of blocks + BLOCK_IT *block_it //block iterator + ); +BOOL8 read_vec_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ); +BOOL8 read_unlv_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ); +#endif diff --git a/ccstruct/callcpp.cpp b/ccstruct/callcpp.cpp new file mode 100644 index 0000000000..af67d75c14 --- /dev/null +++ b/ccstruct/callcpp.cpp @@ -0,0 +1,270 @@ +/********************************************************************** + * File: callcpp.cpp + * Description: extern C interface calling C++ from C. + * Author: Ray Smith + * Created: Sun Feb 04 20:39:23 MST 1996 + * + * (C) Copyright 1996, Hewlett-Packard Co. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "errcode.h" +#ifdef __UNIX__ +#include +#include +#endif +#include +#include "memry.h" +#include "grphics.h" +#include "evnts.h" +#include "varable.h" +#include "callcpp.h" +#include "tprintf.h" +//#include "strace.h" +#include "host.h" + +//extern "C" { + +INT_VAR (tess_cp_mapping0, 0, "Mappings for class pruner distance"); +INT_VAR (tess_cp_mapping1, 1, "Mappings for class pruner distance"); +INT_VAR (tess_cp_mapping2, 2, "Mappings for class pruner distance"); +INT_VAR (tess_cp_mapping3, 3, "Mappings for class pruner distance"); +INT_VAR (stopper_numbers_on, 0, "Allow numbers to be acceptable choices"); +INT_VAR (config_pruner_enabled, 0, "Turn on config pruner"); +INT_VAR (feature_prune_percentile, 0, "Percent of features to use"); +INT_VAR (newcp_ratings_on, 0, "Use new class pruner normalisation"); +INT_VAR (record_matcher_output, 0, "Record detailed matcher info"); +INT_VAR (il1_adaption_test, 0, "Dont adapt to i/I at beginning of word"); +double_VAR (permuter_pending_threshold, 0.0, +"Worst conf for using pending dictionary"); +double_VAR (newcp_duff_rating, 0.30, "Worst rating for calling real matcher"); +double_VAR (newcp_prune_threshold, 1.2, "Ratio of best to prune"); +double_VAR (tessedit_cp_ratio, 0.0, "Ratio from best to prune"); +//Global matcher info from the class pruner. +INT32 cp_classes; +INT32 cp_bestindex; +INT32 cp_bestrating; +INT32 cp_bestconf; +char cp_chars[2]; +INT32 cp_ratings[2]; +INT32 cp_confs[2]; +INT32 cp_maps[4]; +//Global info to control writes of matcher info +INT32 blob_type; //write control +char blob_answer; //correct char +char *word_answer; //correct word +INT32 matcher_pass; //pass in chopper.c +INT32 bits_in_states; //no of bits in states + +#ifndef __UNIX__ +/********************************************************************** + * assert + * + * A version of assert for C on NT. + **********************************************************************/ + +void assert( //recog one owrd + int testing //assert fail if false + ) { + ASSERT_HOST(testing); +} +#endif + +void setup_cp_maps() { + cp_maps[0] = tess_cp_mapping0; + cp_maps[1] = tess_cp_mapping1; + cp_maps[2] = tess_cp_mapping2; + cp_maps[3] = tess_cp_mapping3; +} + + +void trace_stack() { //Trace current stack +} + + +void +cprintf ( //Trace printf +const char *format, ... //special message +) { + va_list args; //variable args + char msg[1000]; + + va_start(args, format); //variable list + vsprintf(msg, format, args); //Format into msg + va_end(args); + + tprintf ("%s", msg); +} + + +char *c_alloc_string( //allocate string + INT32 count //no of chars required + ) { + return alloc_string (count); +} + + +void c_free_string( //free a string + char *string //string to free + ) { + free_string(string); +} + + +void *c_alloc_struct( //allocate memory + INT32 count, //no of chars required + const char *name //class name + ) { + return alloc_struct (count, name); +} + + +void c_free_struct( //free a structure + void *deadstruct, //structure to free + INT32 count, //no of bytes + const char *name //class name + ) { + free_struct(deadstruct, count, name); +} + + +void *c_alloc_mem_p( //allocate permanent space + INT32 count //block size to allocate + ) { + return alloc_mem_p (count); +} + + +void *c_alloc_mem( //get some memory + INT32 count //no of bytes to get + ) { + return alloc_mem (count); +} + + +void c_free_mem( //free mem from alloc_mem + void *oldchunk //chunk to free + ) { + free_mem(oldchunk); +} + + +void c_check_mem( //check consistency + const char *string, //context message + INT8 level //level of check + ) { + check_mem(string, level); +} + +#ifndef GRAPHICS_DISABLED +void *c_create_window( /*create a window */ + const char *name, /*name/title of window */ + INT16 xpos, /*coords of window */ + INT16 ypos, /*coords of window */ + INT16 xsize, /*size of window */ + INT16 ysize, /*size of window */ + double xmin, /*scrolling limits */ + double xmax, /*to stop users */ + double ymin, /*getting lost in */ + double ymax /*empty space */ + ) { + return create_window (name, SCROLLINGWIN, xpos, ypos, xsize, ysize, + xmin, xmax, ymin, ymax, TRUE, FALSE, FALSE, TRUE); +} + + +void c_line_color_index( /*set color */ + void *win, + C_COL index) { + WINDOW window = (WINDOW) win; + + // ASSERT_HOST(index>=0 && index<=48); + if (index < 0 || index > 48) + index = (C_COL) 1; + window->Line_color_index ((COLOUR) index); +} + + +void c_move( /*move pen */ + void *win, + double x, + double y) { + WINDOW window = (WINDOW) win; + + window->Move2d (x, y); +} + + +void c_draw( /*move pen */ + void *win, + double x, + double y) { + WINDOW window = (WINDOW) win; + + window->Draw2d (x, y); +} + + +void c_make_current( /*move pen */ + void *win) { + WINDOW window = (WINDOW) win; + + window->Make_picture_current (); +} + + +void c_clear_window( /*move pen */ + void *win) { + WINDOW window = (WINDOW) win; + + window->Clear_view_surface (); +} + + +char window_wait( /*move pen */ + void *win) { + WINDOW window = (WINDOW) win; + GRAPHICS_EVENT event; + + await_event(window, TRUE, ANY_EVENT, &event); + if (event.type == KEYPRESS_EVENT) + return event.key; + else + return '\0'; +} +#endif + +void reverse32(void *ptr) { + char tmp; + char *cptr = (char *) ptr; + + tmp = *cptr; + *cptr = *(cptr + 3); + *(cptr + 3) = tmp; + tmp = *(cptr + 1); + *(cptr + 1) = *(cptr + 2); + *(cptr + 2) = tmp; +} + + +void reverse16(void *ptr) { + char tmp; + char *cptr = (char *) ptr; + + tmp = *cptr; + *cptr = *(cptr + 1); + *(cptr + 1) = tmp; +} + + +//}; diff --git a/ccstruct/coutln.cpp b/ccstruct/coutln.cpp new file mode 100644 index 0000000000..6579eeb58e --- /dev/null +++ b/ccstruct/coutln.cpp @@ -0,0 +1,604 @@ +/********************************************************************** + * File: coutln.c (Formerly coutline.c) + * Description: Code for the C_OUTLINE class. + * Author: Ray Smith + * Created: Mon Oct 07 16:01:57 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#ifdef __UNIX__ +#include +#endif +#include "coutln.h" + +ELISTIZE_S (C_OUTLINE) +ICOORD C_OUTLINE::step_coords[4] = { + ICOORD (-1, 0), ICOORD (0, -1), ICOORD (1, 0), ICOORD (0, 1) +}; + +/********************************************************************** + * C_OUTLINE::C_OUTLINE + * + * Constructor to build a C_OUTLINE from a CRACKEDGE LOOP. + **********************************************************************/ + +C_OUTLINE::C_OUTLINE ( +//constructor +CRACKEDGE * startpt, //outline to convert +ICOORD bot_left, //bounding box +ICOORD top_right, INT16 length //length of loop +):box (bot_left, top_right), start (startpt->pos) { + INT16 stepindex; //index to step + CRACKEDGE *edgept; //current point + + stepcount = length; //no of steps + //get memory + steps = (UINT8 *) alloc_mem (step_mem()); + memset(steps, 0, step_mem()); + edgept = startpt; + + for (stepindex = 0; stepindex < length; stepindex++) { + //set compact step + set_step (stepindex, edgept->stepdir); + edgept = edgept->next; + } +} + + +/********************************************************************** + * C_OUTLINE::C_OUTLINE + * + * Constructor to build a C_OUTLINE from a C_OUTLINE_FRAG. + **********************************************************************/ +C_OUTLINE::C_OUTLINE ( +//constructor + //steps to copy +ICOORD startpt, DIR128 * new_steps, +INT16 length //length of loop +):start (startpt) { + INT8 dirdiff; //direction difference + DIR128 prevdir; //previous direction + DIR128 dir; //current direction + DIR128 lastdir; //dir of last step + BOX new_box; //easy bounding + INT16 stepindex; //index to step + INT16 srcindex; //source steps + ICOORD pos; //current position + + pos = startpt; + stepcount = length; //no of steps + //get memory + steps = (UINT8 *) alloc_mem (step_mem()); + memset(steps, 0, step_mem()); + + lastdir = new_steps[length - 1]; + prevdir = lastdir; + for (stepindex = 0, srcindex = 0; srcindex < length; + stepindex++, srcindex++) { + new_box = BOX (pos, pos); + box += new_box; + //copy steps + dir = new_steps[srcindex]; + set_step(stepindex, dir); + dirdiff = dir - prevdir; + pos += step (stepindex); + if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) { + stepindex -= 2; //cancel there-and-back + prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir; + } + else + prevdir = dir; + } + ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ()); + do { + dirdiff = step_dir (stepindex - 1) - step_dir (0); + if (dirdiff == 64 || dirdiff == -64) { + start += step (0); + stepindex -= 2; //cancel there-and-back + for (int i = 0; i < stepindex; ++i) + set_step(i, step_dir(i + 1)); + } + } + while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64)); + stepcount = stepindex; + ASSERT_HOST (stepcount >= 4); +} + +/********************************************************************** + * C_OUTLINE::C_OUTLINE + * + * Constructor to build a C_OUTLINE from a rotation of a C_OUTLINE. + **********************************************************************/ + +C_OUTLINE::C_OUTLINE( //constructor + C_OUTLINE *srcline, //outline to + FCOORD rotation //rotate + ) { + BOX new_box; //easy bounding + INT16 stepindex; //index to step + INT16 dirdiff; //direction change + ICOORD pos; //current position + ICOORD prevpos; //previous dest point + + ICOORD destpos; //destination point + INT16 destindex; //index to step + DIR128 dir; //coded direction + UINT8 new_step; + + stepcount = srcline->stepcount * 2; + //get memory + steps = (UINT8 *) alloc_mem (step_mem()); + memset(steps, 0, step_mem()); + + for (int iteration = 0; iteration < 2; ++iteration) { + DIR128 round1 = iteration == 0 ? 32 : 0; + DIR128 round2 = iteration != 0 ? 32 : 0; + pos = srcline->start; + prevpos = pos; + prevpos.rotate (rotation); + start = prevpos; + box = BOX (start, start); + destindex = 0; + for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) { + pos += srcline->step (stepindex); + destpos = pos; + destpos.rotate (rotation); + if (destpos.x () != prevpos.x () || destpos.y () != prevpos.y ()) { + dir = DIR128 (FCOORD (destpos - prevpos)); + dir += 64; //turn to step style + new_step = dir.get_dir (); + if (new_step & 31) { + set_step(destindex++, dir + round1); + if (destindex < 2 + || (dirdiff = + step_dir (destindex - 1) - step_dir (destindex - 2)) != + -64 && dirdiff != 64) + set_step(destindex++, dir + round2); + else { + set_step(destindex - 1, dir + round2); + set_step(destindex++, dir + round1); + } + } + else { + set_step(destindex++, dir); + if (destindex >= 2 + && + ((dirdiff = + step_dir (destindex - 1) - step_dir (destindex - 2)) == + -64 || dirdiff == 64)) + destindex -= 2; // Forget u turn + } + prevpos = destpos; + new_box = BOX (destpos, destpos); + box += new_box; + } + } + ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ()); + dirdiff = step_dir (destindex - 1) - step_dir (0); + while ((dirdiff == 64 || dirdiff == -64) && destindex > 1) { + start += step (0); + destindex -= 2; + for (int i = 0; i < destindex; ++i) + set_step(i, step_dir(i + 1)); + dirdiff = step_dir (destindex - 1) - step_dir (0); + } + if (destindex >= 4) + break; + } + stepcount = destindex; + destpos = start; + for (stepindex = 0; stepindex < stepcount; stepindex++) { + destpos += step (stepindex); + } + ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ()); +} + + +/********************************************************************** + * C_OUTLINE::area + * + * Compute the area of the outline. + **********************************************************************/ + +INT32 C_OUTLINE::area() { //winding number + int stepindex; //current step + INT32 total_steps; //steps to do + INT32 total; //total area + ICOORD pos; //position of point + ICOORD next_step; //step to next pix + C_OUTLINE_IT it = child (); + + pos = start_pos (); + total_steps = pathlength (); + total = 0; + for (stepindex = 0; stepindex < total_steps; stepindex++) { + //all intersected + next_step = step (stepindex); + if (next_step.x () < 0) + total += pos.y (); + else if (next_step.x () > 0) + total -= pos.y (); + pos += next_step; + } + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + total += it.data ()->area ();//add areas of children + + return total; +} + + +/********************************************************************** + * C_OUTLINE::outer_area + * + * Compute the area of the outline. + **********************************************************************/ + +INT32 C_OUTLINE::outer_area() { //winding number + int stepindex; //current step + INT32 total_steps; //steps to do + INT32 total; //total area + ICOORD pos; //position of point + ICOORD next_step; //step to next pix + + pos = start_pos (); + total_steps = pathlength (); + total = 0; + for (stepindex = 0; stepindex < total_steps; stepindex++) { + //all intersected + next_step = step (stepindex); + if (next_step.x () < 0) + total += pos.y (); + else if (next_step.x () > 0) + total -= pos.y (); + pos += next_step; + } + + return total; +} + + +/********************************************************************** + * C_OUTLINE::count_transitions + * + * Compute the number of x and y maxes and mins in the outline. + **********************************************************************/ + +INT32 C_OUTLINE::count_transitions( //winding number + INT32 threshold //on size + ) { + BOOL8 first_was_max_x; //what was first + BOOL8 first_was_max_y; + BOOL8 looking_for_max_x; //what is next + BOOL8 looking_for_min_x; + BOOL8 looking_for_max_y; //what is next + BOOL8 looking_for_min_y; + int stepindex; //current step + INT32 total_steps; //steps to do + //current limits + INT32 max_x, min_x, max_y, min_y; + INT32 initial_x, initial_y; //initial limits + INT32 total; //total changes + ICOORD pos; //position of point + ICOORD next_step; //step to next pix + + pos = start_pos (); + total_steps = pathlength (); + total = 0; + max_x = min_x = pos.x (); + max_y = min_y = pos.y (); + looking_for_max_x = TRUE; + looking_for_min_x = TRUE; + looking_for_max_y = TRUE; + looking_for_min_y = TRUE; + first_was_max_x = FALSE; + first_was_max_y = FALSE; + initial_x = pos.x (); + initial_y = pos.y (); //stop uninit warning + for (stepindex = 0; stepindex < total_steps; stepindex++) { + //all intersected + next_step = step (stepindex); + pos += next_step; + if (next_step.x () < 0) { + if (looking_for_max_x && pos.x () < min_x) + min_x = pos.x (); + if (looking_for_min_x && max_x - pos.x () > threshold) { + if (looking_for_max_x) { + initial_x = max_x; + first_was_max_x = FALSE; + } + total++; + looking_for_max_x = TRUE; + looking_for_min_x = FALSE; + min_x = pos.x (); //reset min + } + } + else if (next_step.x () > 0) { + if (looking_for_min_x && pos.x () > max_x) + max_x = pos.x (); + if (looking_for_max_x && pos.x () - min_x > threshold) { + if (looking_for_min_x) { + initial_x = min_x; //remember first min + first_was_max_x = TRUE; + } + total++; + looking_for_max_x = FALSE; + looking_for_min_x = TRUE; + max_x = pos.x (); + } + } + else if (next_step.y () < 0) { + if (looking_for_max_y && pos.y () < min_y) + min_y = pos.y (); + if (looking_for_min_y && max_y - pos.y () > threshold) { + if (looking_for_max_y) { + initial_y = max_y; //remember first max + first_was_max_y = FALSE; + } + total++; + looking_for_max_y = TRUE; + looking_for_min_y = FALSE; + min_y = pos.y (); //reset min + } + } + else { + if (looking_for_min_y && pos.y () > max_y) + max_y = pos.y (); + if (looking_for_max_y && pos.y () - min_y > threshold) { + if (looking_for_min_y) { + initial_y = min_y; //remember first min + first_was_max_y = TRUE; + } + total++; + looking_for_max_y = FALSE; + looking_for_min_y = TRUE; + max_y = pos.y (); + } + } + + } + if (first_was_max_x && looking_for_min_x) { + if (max_x - initial_x > threshold) + total++; + else + total--; + } + else if (!first_was_max_x && looking_for_max_x) { + if (initial_x - min_x > threshold) + total++; + else + total--; + } + if (first_was_max_y && looking_for_min_y) { + if (max_y - initial_y > threshold) + total++; + else + total--; + } + else if (!first_was_max_y && looking_for_max_y) { + if (initial_y - min_y > threshold) + total++; + else + total--; + } + + return total; +} + + +/********************************************************************** + * C_OUTLINE::operator< + * + * Return TRUE if the left operand is inside the right one. + **********************************************************************/ + +BOOL8 +C_OUTLINE::operator< ( //winding number +const C_OUTLINE & other //other outline +) const +{ + INT16 count = 0; //winding count + ICOORD pos; //position of point + INT32 stepindex; //index to cstep + + if (!box.overlap (other.box)) + return FALSE; //can't be contained + + pos = start; + for (stepindex = 0; stepindex < stepcount + && (count = other.winding_number (pos)) == INTERSECTING; stepindex++) + pos += step (stepindex); //try all points + if (count == INTERSECTING) { + //all intersected + pos = other.start; + for (stepindex = 0; stepindex < other.stepcount + && (count = winding_number (pos)) == INTERSECTING; stepindex++) + //try other way round + pos += other.step (stepindex); + return count == INTERSECTING || count == 0; + } + return count != 0; +} + + +/********************************************************************** + * C_OUTLINE::winding_number + * + * Return the winding number of the outline around the given point. + **********************************************************************/ + +INT16 C_OUTLINE::winding_number( //winding number + ICOORD point //point to wind around + ) const { + INT16 stepindex; //index to cstep + INT16 count; //winding count + ICOORD vec; //to current point + ICOORD stepvec; //step vector + INT32 cross; //cross product + + vec = start - point; //vector to it + count = 0; + for (stepindex = 0; stepindex < stepcount; stepindex++) { + stepvec = step (stepindex); //get the step + //crossing the line + if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) { + cross = vec * stepvec; //cross product + if (cross > 0) + count++; //crossing right half + else if (cross == 0) + return INTERSECTING; //going through point + } + else if (vec.y () > 0 && vec.y () + stepvec.y () <= 0) { + cross = vec * stepvec; + if (cross < 0) + count--; //crossing back + else if (cross == 0) + return INTERSECTING; //illegal + } + vec += stepvec; //sum vectors + } + return count; //winding number +} + + +/********************************************************************** + * C_OUTLINE::turn_direction + * + * Return the sum direction delta of the outline. + **********************************************************************/ + +INT16 C_OUTLINE::turn_direction() const { //winding number + DIR128 prevdir; //previous direction + DIR128 dir; //current direction + INT16 stepindex; //index to cstep + INT8 dirdiff; //direction difference + INT16 count; //winding count + + count = 0; + prevdir = step_dir (stepcount - 1); + for (stepindex = 0; stepindex < stepcount; stepindex++) { + dir = step_dir (stepindex); + dirdiff = dir - prevdir; + ASSERT_HOST (dirdiff == 0 || dirdiff == 32 || dirdiff == -32); + count += dirdiff; + prevdir = dir; + } + ASSERT_HOST (count == 128 || count == -128); + return count; //winding number +} + + +/********************************************************************** + * C_OUTLINE::reverse + * + * Reverse the direction of an outline. + **********************************************************************/ + +void C_OUTLINE::reverse() { //reverse drection + DIR128 halfturn = MODULUS / 2; //amount to shift + DIR128 stepdir; //direction of step + INT16 stepindex; //index to cstep + INT16 farindex; //index to other side + INT16 halfsteps; //half of stepcount + + halfsteps = (stepcount + 1) / 2; + for (stepindex = 0; stepindex < halfsteps; stepindex++) { + farindex = stepcount - stepindex - 1; + stepdir = step_dir (stepindex); + set_step (stepindex, step_dir (farindex) + halfturn); + set_step (farindex, stepdir + halfturn); + } +} + + +/********************************************************************** + * C_OUTLINE::move + * + * Move C_OUTLINE by vector + **********************************************************************/ + +void C_OUTLINE::move( // reposition OUTLINE + const ICOORD vec // by vector + ) { + C_OUTLINE_IT it(&children); // iterator + + box.move (vec); + start += vec; + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->move (vec); // move child outlines +} + + +/********************************************************************** + * C_OUTLINE::plot + * + * Draw the outline in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void C_OUTLINE::plot( //draw it + WINDOW window, //window to draw in + COLOUR colour //colour to draw in + ) const { + INT16 stepindex; //index to cstep + ICOORD pos; //current position + DIR128 stepdir; //direction of step + DIR128 oldstepdir; //previous stepdir + + pos = start; //current position + line_color_index(window, colour); + move2d (window, pos.x (), pos.y ()); + stepindex = 0; + stepdir = step_dir (0); //get direction + while (stepindex < stepcount) { + do { + pos += step (stepindex); //step to next + stepindex++; //count steps + oldstepdir = stepdir; + //new direction + stepdir = step_dir (stepindex); + } + while (stepindex < stepcount + && oldstepdir.get_dir () == stepdir.get_dir ()); + //merge straight lines + draw2d (window, pos.x (), pos.y ()); + } +} +#endif + + +/********************************************************************** + * C_OUTLINE::operator= + * + * Assignment - deep copy data + **********************************************************************/ + + //assignment +C_OUTLINE & C_OUTLINE::operator= ( +const C_OUTLINE & source //from this +) { + box = source.box; + start = source.start; + if (steps != NULL) + free_mem(steps); + stepcount = source.stepcount; + steps = (UINT8 *) alloc_mem (step_mem()); + memmove (steps, source.steps, step_mem()); + if (!children.empty ()) + children.clear (); + children.deep_copy (&source.children); + return *this; +} diff --git a/ccstruct/coutln.h b/ccstruct/coutln.h new file mode 100644 index 0000000000..1f35d4aa51 --- /dev/null +++ b/ccstruct/coutln.h @@ -0,0 +1,176 @@ +/********************************************************************** + * File: coutln.c (Formerly: coutline.c) + * Description: Code for the C_OUTLINE class. + * Author: Ray Smith + * Created: Mon Oct 07 16:01:57 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef COUTLN_H +#define COUTLN_H + +#include "grphics.h" +#include "crakedge.h" +#include "mod128.h" +#include "bits16.h" +#include "rect.h" +#include "blckerr.h" + +#define INTERSECTING MAX_INT16//no winding number + + //mask to get step +#define STEP_MASK 3 + +enum C_OUTLINE_FLAGS +{ + COUT_INVERSE //White on black blob +}; + +class DLLSYM C_OUTLINE; //forward declaration + +ELISTIZEH_S (C_OUTLINE) +class DLLSYM C_OUTLINE:public ELIST_LINK +{ + public: + C_OUTLINE() { //empty constructor + steps = NULL; + } + C_OUTLINE( //constructor + CRACKEDGE *startpt, //from edge detector + ICOORD bot_left, //bounding box //length of loop + ICOORD top_right, + INT16 length); + C_OUTLINE(ICOORD startpt, //start of loop + DIR128 *new_steps, //steps in loop + INT16 length); //length of loop + //outline to copy + C_OUTLINE(C_OUTLINE *srcline, FCOORD rotation); //and rotate + ~C_OUTLINE () { //destructor + if (steps != NULL) + free_mem(steps); + steps = NULL; + } + + BOOL8 flag( //test flag + C_OUTLINE_FLAGS mask) const { //flag to test + return flags.bit (mask); + } + void set_flag( //set flag value + C_OUTLINE_FLAGS mask, //flag to test + BOOL8 value) { //value to set + flags.set_bit (mask, value); + } + + C_OUTLINE_LIST *child() { //get child list + return &children; + } + + //access function + const BOX &bounding_box() const { + return box; + } + void set_step( //set a step + INT16 stepindex, //index of step + INT8 stepdir) { //chain code + int shift = stepindex%4 * 2; + UINT8 mask = 3 << shift; + steps[stepindex/4] = ((stepdir << shift) & mask) | + (steps[stepindex/4] & ~mask); + //squeeze 4 into byte + } + void set_step( //set a step + INT16 stepindex, //index of step + DIR128 stepdir) { //direction + //clean it + INT8 chaindir = stepdir.get_dir() >> (DIRBITS - 2); + //difference + set_step(stepindex, chaindir); + //squeeze 4 into byte + } + + //get start position + const ICOORD &start_pos() const { + return start; + } + INT32 pathlength() const { //get path length + return stepcount; + } + // Return step at a given index as a DIR128. + DIR128 step_dir(INT16 index) const { + return DIR128((INT16)(((steps[index/4] >> (index%4 * 2)) & STEP_MASK) << + (DIRBITS - 2))); + } + // Return the step vector for the given outline position. + ICOORD step(INT16 index) const { //index of step + return step_coords[(steps[index/4] >> (index%4 * 2)) & STEP_MASK]; + } + + INT32 area(); //return area + INT32 outer_area(); //return area + INT32 count_transitions( //count maxima + INT32 threshold); //size threshold + + BOOL8 operator< ( //containment test + const C_OUTLINE & other) const; + BOOL8 operator> ( //containment test + C_OUTLINE & other) const + { + return other < *this; //use the < to do it + } + INT16 winding_number( //get winding number + ICOORD testpt) const; //around this point + //get direction + INT16 turn_direction() const; + void reverse(); //reverse direction + + void move( // reposition outline + const ICOORD vec); // by vector + + void plot( //draw one + WINDOW window, //window to draw in + COLOUR colour) const; //colour to draw it + + void prep_serialise() { //set ptrs to counts + children.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + //stepcount = # bytes + serialise_bytes (f, (void *) steps, step_mem()); + children.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + steps = (UINT8 *) de_serialise_bytes (f, step_mem()); + children.de_dump (f); + } + + //assignment + make_serialise (C_OUTLINE) C_OUTLINE & operator= ( + const C_OUTLINE & source); //from this + + private: + int step_mem() const { return (stepcount+3) / 4; } + + BOX box; //boudning box + ICOORD start; //start coord + UINT8 *steps; //step array + INT16 stepcount; //no of steps + BITS16 flags; //flags about outline + C_OUTLINE_LIST children; //child elements + static ICOORD step_coords[4]; +}; +#endif diff --git a/ccstruct/crakedge.h b/ccstruct/crakedge.h new file mode 100644 index 0000000000..5dd6917c54 --- /dev/null +++ b/ccstruct/crakedge.h @@ -0,0 +1,39 @@ +/********************************************************************** + * File: crakedge.h (Formerly: crkedge.h) + * Description: Sturctures for the Crack following edge detector. + * Author: Ray Smith + * Created: Fri Mar 22 16:06:38 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef CRAKEDGE_H +#define CRAKEDGE_H + +#include "points.h" +#include "mod128.h" + +class CRACKEDGE +{ + public: + ICOORD pos; /*position of crack */ + INT8 stepx; //edge step + INT8 stepy; + INT8 stepdir; //chaincode + CRACKEDGE *prev; /*previous point */ + CRACKEDGE *next; /*next point */ + + NEWDELETE2 (CRACKEDGE) CRACKEDGE () { + } //empty constructor +}; +#endif diff --git a/ccstruct/genblob.cpp b/ccstruct/genblob.cpp new file mode 100644 index 0000000000..bd461b95e2 --- /dev/null +++ b/ccstruct/genblob.cpp @@ -0,0 +1,133 @@ +/********************************************************************** + * File: genblob.cpp (Formerly gblob.c) + * Description: Generic Blob processing routines + * Author: Phil Cheatle + * Created: Mon Nov 25 10:53:26 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "stepblob.h" +#include "polyblob.h" +#include "genblob.h" + +/********************************************************************** + * blob_comparator() + * + * Blob comparator used to sort a blob list so that blobs are in increasing + * order of left edge. + **********************************************************************/ + +int blob_comparator( //sort blobs + const void *blob1p, //ptr to ptr to blob1 + const void *blob2p //ptr to ptr to blob2 + ) { + PBLOB *blob1 = *(PBLOB **) blob1p; + PBLOB *blob2 = *(PBLOB **) blob2p; + + return blob1->bounding_box ().left () - blob2->bounding_box ().left (); +} + + +/********************************************************************** + * c_blob_comparator() + * + * Blob comparator used to sort a blob list so that blobs are in increasing + * order of left edge. + **********************************************************************/ + +int c_blob_comparator( //sort blobs + const void *blob1p, //ptr to ptr to blob1 + const void *blob2p //ptr to ptr to blob2 + ) { + C_BLOB *blob1 = *(C_BLOB **) blob1p; + C_BLOB *blob2 = *(C_BLOB **) blob2p; + + return blob1->bounding_box ().left () - blob2->bounding_box ().left (); +} + + +/********************************************************************** + * gblob_bounding_box() + * + * Return the bounding box of a generic blob. + **********************************************************************/ + +BOX gblob_bounding_box( //Get bounding box + PBLOB *blob, //generic blob + BOOL8 polygonal //is blob polygonal? + ) { + if (polygonal) + return blob->bounding_box (); + else + return ((C_BLOB *) blob)->bounding_box (); +} + + +/********************************************************************** + * gblob_sort_list() + * + * Sort a generic blob list into order of bounding box left edge + **********************************************************************/ + +void gblob_sort_list( //Sort a gblob list + PBLOB_LIST *blob_list, //generic blob list + BOOL8 polygonal //is list polygonal? + ) { + PBLOB_IT b_it; + C_BLOB_IT c_it; + + if (polygonal) { + b_it.set_to_list (blob_list); + b_it.sort (blob_comparator); + } + else { + c_it.set_to_list ((C_BLOB_LIST *) blob_list); + c_it.sort (c_blob_comparator); + } +} + + +/********************************************************************** + * gblob_out_list() + * + * Return the generic outline list of a generic blob. + **********************************************************************/ + +OUTLINE_LIST *gblob_out_list( //Get outline list + PBLOB *blob, //generic blob + BOOL8 polygonal //is blob polygonal? + ) { + if (polygonal) + return blob->out_list (); + else + return (OUTLINE_LIST *) ((C_BLOB *) blob)->out_list (); +} + + +/********************************************************************** + * goutline_bounding_box() + * + * Return the bounding box of a generic outline. + **********************************************************************/ + +BOX goutline_bounding_box( //Get bounding box + OUTLINE *outline, //generic outline + BOOL8 polygonal //is outline polygonal? + ) { + if (polygonal) + return outline->bounding_box (); + else + return ((C_OUTLINE *) outline)->bounding_box (); +} diff --git a/ccstruct/genblob.h b/ccstruct/genblob.h new file mode 100644 index 0000000000..eaf17ef92f --- /dev/null +++ b/ccstruct/genblob.h @@ -0,0 +1,52 @@ +/********************************************************************** + * File: genblob.h (Formerly gblob.h) + * Description: Generic Blob processing routines + * Author: Phil Cheatle + * Created: Mon Nov 25 10:53:26 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef GENBLOB_H +#define GENBLOB_H + +#include "polyblob.h" +#include "hosthplb.h" +#include "rect.h" +#include "notdll.h" + +int blob_comparator( //sort blobs + const void *blob1p, //ptr to ptr to blob1 + const void *blob2p //ptr to ptr to blob2 + ); +int c_blob_comparator( //sort blobs + const void *blob1p, //ptr to ptr to blob1 + const void *blob2p //ptr to ptr to blob2 + ); +BOX gblob_bounding_box( //Get bounding box + PBLOB *blob, //generic blob + BOOL8 polygonal //is blob polygonal? + ); +void gblob_sort_list( //Sort a gblob list + PBLOB_LIST *blob_list, //generic blob list + BOOL8 polygonal //is list polygonal? + ); +OUTLINE_LIST *gblob_out_list( //Get outline list + PBLOB *blob, //generic blob + BOOL8 polygonal //is blob polygonal? + ); +BOX goutline_bounding_box( //Get bounding box + OUTLINE *outline, //generic outline + BOOL8 polygonal //is outline polygonal? + ); +#endif diff --git a/ccstruct/hpddef.h b/ccstruct/hpddef.h new file mode 100644 index 0000000000..bb90c4283c --- /dev/null +++ b/ccstruct/hpddef.h @@ -0,0 +1,39 @@ +/********************************************************************** + * File: hpddef.h + * Description: Defines for dll symbols for handpd.dll. + * Author: Ray Smith + * Created: Tue Apr 30 17:15:01 MDT 1996 + * + * (C) Copyright 1996, Hewlett-Packard Co. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +//This file does NOT use the usual single inclusion code as it +//is necessary to allow it to be executed every time it is included. +//#ifndef HPDDEF_H +//#define HPDDEF_H + +#undef DLLSYM +#ifndef __IPEDLL +# define DLLSYM +#else +# ifdef __BUILDING_HANDPD__ +# define DLLSYM DLLEXPORT +# else +# define DLLSYM DLLIMPORT +# endif +#endif +#if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__) +# pragma import on +#endif + +//#endif diff --git a/ccstruct/hpdsizes.h b/ccstruct/hpdsizes.h new file mode 100644 index 0000000000..2670e21b07 --- /dev/null +++ b/ccstruct/hpdsizes.h @@ -0,0 +1,8 @@ +#ifndef HPDSIZES_H +#define HPDSIZES_H + +#define NUM_TEXT_ATTR 10 +#define NUM_BLOCK_ATTR 7 +#define MAXLENGTH 128 +#define NUM_BACKGROUNDS 8 +#endif diff --git a/ccstruct/ipoints.h b/ccstruct/ipoints.h new file mode 100644 index 0000000000..fe62d12978 --- /dev/null +++ b/ccstruct/ipoints.h @@ -0,0 +1,479 @@ +/********************************************************************** + * File: ipoints.h (Formerly icoords.h) + * Description: Inline functions for coords.h. + * Author: Ray Smith + * Created: Fri Jun 21 15:14:21 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef IPOINTS_H +#define IPOINTS_H + +#include + +/********************************************************************** + * operator! + * + * Rotate an ICOORD 90 degrees anticlockwise. + **********************************************************************/ + +inline ICOORD +operator! ( //rotate 90 deg anti +const ICOORD & src //thing to rotate +) { + ICOORD result; //output + + result.xcoord = -src.ycoord; + result.ycoord = src.xcoord; + return result; +} + + +/********************************************************************** + * operator- + * + * Unary minus of an ICOORD. + **********************************************************************/ + +inline ICOORD +operator- ( //unary minus +const ICOORD & src //thing to minus +) { + ICOORD result; //output + + result.xcoord = -src.xcoord; + result.ycoord = -src.ycoord; + return result; +} + + +/********************************************************************** + * operator+ + * + * Add 2 ICOORDS. + **********************************************************************/ + +inline ICOORD +operator+ ( //sum vectors +const ICOORD & op1, //operands +const ICOORD & op2) { + ICOORD sum; //result + + sum.xcoord = op1.xcoord + op2.xcoord; + sum.ycoord = op1.ycoord + op2.ycoord; + return sum; +} + + +/********************************************************************** + * operator+= + * + * Add 2 ICOORDS. + **********************************************************************/ + +inline ICOORD & +operator+= ( //sum vectors +ICOORD & op1, //operands +const ICOORD & op2) { + op1.xcoord += op2.xcoord; + op1.ycoord += op2.ycoord; + return op1; +} + + +/********************************************************************** + * operator- + * + * Subtract 2 ICOORDS. + **********************************************************************/ + +inline ICOORD +operator- ( //subtract vectors +const ICOORD & op1, //operands +const ICOORD & op2) { + ICOORD sum; //result + + sum.xcoord = op1.xcoord - op2.xcoord; + sum.ycoord = op1.ycoord - op2.ycoord; + return sum; +} + + +/********************************************************************** + * operator-= + * + * Subtract 2 ICOORDS. + **********************************************************************/ + +inline ICOORD & +operator-= ( //sum vectors +ICOORD & op1, //operands +const ICOORD & op2) { + op1.xcoord -= op2.xcoord; + op1.ycoord -= op2.ycoord; + return op1; +} + + +/********************************************************************** + * operator% + * + * Scalar product of 2 ICOORDS. + **********************************************************************/ + +inline INT32 +operator% ( //scalar product +const ICOORD & op1, //operands +const ICOORD & op2) { + return op1.xcoord * op2.xcoord + op1.ycoord * op2.ycoord; +} + + +/********************************************************************** + * operator* + * + * Cross product of 2 ICOORDS. + **********************************************************************/ + +inline INT32 operator *( //cross product + const ICOORD &op1, //operands + const ICOORD &op2) { + return op1.xcoord * op2.ycoord - op1.ycoord * op2.xcoord; +} + + +/********************************************************************** + * operator* + * + * Scalar multiply of an ICOORD. + **********************************************************************/ + +inline ICOORD operator *( //scalar multiply + const ICOORD &op1, //operands + INT16 scale) { + ICOORD result; //output + + result.xcoord = op1.xcoord * scale; + result.ycoord = op1.ycoord * scale; + return result; +} + + +inline ICOORD operator *( //scalar multiply + INT16 scale, + const ICOORD &op1 //operands + ) { + ICOORD result; //output + + result.xcoord = op1.xcoord * scale; + result.ycoord = op1.ycoord * scale; + return result; +} + + +/********************************************************************** + * operator*= + * + * Scalar multiply of an ICOORD. + **********************************************************************/ + +inline ICOORD & +operator*= ( //scalar multiply +ICOORD & op1, //operands +INT16 scale) { + op1.xcoord *= scale; + op1.ycoord *= scale; + return op1; +} + + +/********************************************************************** + * operator/ + * + * Scalar divide of an ICOORD. + **********************************************************************/ + +inline ICOORD +operator/ ( //scalar divide +const ICOORD & op1, //operands +INT16 scale) { + ICOORD result; //output + + result.xcoord = op1.xcoord / scale; + result.ycoord = op1.ycoord / scale; + return result; +} + + +/********************************************************************** + * operator/= + * + * Scalar divide of an ICOORD. + **********************************************************************/ + +inline ICOORD & +operator/= ( //scalar divide +ICOORD & op1, //operands +INT16 scale) { + op1.xcoord /= scale; + op1.ycoord /= scale; + return op1; +} + + +/********************************************************************** + * ICOORD::rotate + * + * Rotate an ICOORD by the given (normalized) (cos,sin) vector. + **********************************************************************/ + +inline void ICOORD::rotate( //rotate by vector + const FCOORD& vec) { + INT16 tmp; + + tmp = (INT16) floor (xcoord * vec.x () - ycoord * vec.y () + 0.5); + ycoord = (INT16) floor (ycoord * vec.x () + xcoord * vec.y () + 0.5); + xcoord = tmp; +} + + +/********************************************************************** + * operator! + * + * Rotate an FCOORD 90 degrees anticlockwise. + **********************************************************************/ + +inline FCOORD +operator! ( //rotate 90 deg anti +const FCOORD & src //thing to rotate +) { + FCOORD result; //output + + result.xcoord = -src.ycoord; + result.ycoord = src.xcoord; + return result; +} + + +/********************************************************************** + * operator- + * + * Unary minus of an FCOORD. + **********************************************************************/ + +inline FCOORD +operator- ( //unary minus +const FCOORD & src //thing to minus +) { + FCOORD result; //output + + result.xcoord = -src.xcoord; + result.ycoord = -src.ycoord; + return result; +} + + +/********************************************************************** + * operator+ + * + * Add 2 FCOORDS. + **********************************************************************/ + +inline FCOORD +operator+ ( //sum vectors +const FCOORD & op1, //operands +const FCOORD & op2) { + FCOORD sum; //result + + sum.xcoord = op1.xcoord + op2.xcoord; + sum.ycoord = op1.ycoord + op2.ycoord; + return sum; +} + + +/********************************************************************** + * operator+= + * + * Add 2 FCOORDS. + **********************************************************************/ + +inline FCOORD & +operator+= ( //sum vectors +FCOORD & op1, //operands +const FCOORD & op2) { + op1.xcoord += op2.xcoord; + op1.ycoord += op2.ycoord; + return op1; +} + + +/********************************************************************** + * operator- + * + * Subtract 2 FCOORDS. + **********************************************************************/ + +inline FCOORD +operator- ( //subtract vectors +const FCOORD & op1, //operands +const FCOORD & op2) { + FCOORD sum; //result + + sum.xcoord = op1.xcoord - op2.xcoord; + sum.ycoord = op1.ycoord - op2.ycoord; + return sum; +} + + +/********************************************************************** + * operator-= + * + * Subtract 2 FCOORDS. + **********************************************************************/ + +inline FCOORD & +operator-= ( //sum vectors +FCOORD & op1, //operands +const FCOORD & op2) { + op1.xcoord -= op2.xcoord; + op1.ycoord -= op2.ycoord; + return op1; +} + + +/********************************************************************** + * operator% + * + * Scalar product of 2 FCOORDS. + **********************************************************************/ + +inline float +operator% ( //scalar product +const FCOORD & op1, //operands +const FCOORD & op2) { + return op1.xcoord * op2.xcoord + op1.ycoord * op2.ycoord; +} + + +/********************************************************************** + * operator* + * + * Cross product of 2 FCOORDS. + **********************************************************************/ + +inline float operator *( //cross product + const FCOORD &op1, //operands + const FCOORD &op2) { + return op1.xcoord * op2.ycoord - op1.ycoord * op2.xcoord; +} + + +/********************************************************************** + * operator* + * + * Scalar multiply of an FCOORD. + **********************************************************************/ + +inline FCOORD operator *( //scalar multiply + const FCOORD &op1, //operands + float scale) { + FCOORD result; //output + + result.xcoord = op1.xcoord * scale; + result.ycoord = op1.ycoord * scale; + return result; +} + + +inline FCOORD operator *( //scalar multiply + float scale, + const FCOORD &op1 //operands + ) { + FCOORD result; //output + + result.xcoord = op1.xcoord * scale; + result.ycoord = op1.ycoord * scale; + return result; +} + + +/********************************************************************** + * operator*= + * + * Scalar multiply of an FCOORD. + **********************************************************************/ + +inline FCOORD & +operator*= ( //scalar multiply +FCOORD & op1, //operands +float scale) { + op1.xcoord *= scale; + op1.ycoord *= scale; + return op1; +} + + +/********************************************************************** + * operator/ + * + * Scalar divide of an FCOORD. + **********************************************************************/ + +inline FCOORD +operator/ ( //scalar divide +const FCOORD & op1, //operands +float scale) { + FCOORD result; //output + + if (scale != 0) { + result.xcoord = op1.xcoord / scale; + result.ycoord = op1.ycoord / scale; + } + return result; +} + + +/********************************************************************** + * operator/= + * + * Scalar divide of an FCOORD. + **********************************************************************/ + +inline FCOORD & +operator/= ( //scalar divide +FCOORD & op1, //operands +float scale) { + if (scale != 0) { + op1.xcoord /= scale; + op1.ycoord /= scale; + } + return op1; +} + + +/********************************************************************** + * rotate + * + * Rotate an FCOORD by the given (normalized) (cos,sin) vector. + **********************************************************************/ + +inline void FCOORD::rotate( //rotate by vector + const FCOORD vec) { + float tmp; + + tmp = xcoord * vec.x () - ycoord * vec.y (); + ycoord = ycoord * vec.x () + xcoord * vec.y (); + xcoord = tmp; +} +#endif diff --git a/ccstruct/labls.cpp b/ccstruct/labls.cpp new file mode 100644 index 0000000000..7e9d48691d --- /dev/null +++ b/ccstruct/labls.cpp @@ -0,0 +1,188 @@ +/********************************************************************** + * File: labls.c (Formerly labels.c) + * Description: Attribute definition tables + * Author: Sheelagh Lloyd? + * Created: + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "hpdsizes.h" +#include "labls.h" + +/****************************************************************************** + * TEXT REGIONS + *****************************************************************************/ +DLLSYM INT32 tn[NUM_TEXT_ATTR] = { + 3, //T_HORIZONTAL + 4, //T_TEXT + 2, //T_SERIF + 2, //T_PROPORTIONAL + 2, //T_NORMAL + 2, //T_UPRIGHT + 2, //T_SOLID + 3, //T_BLACK + 2, //T_NOTUNDER + 2, //T_NOTDROP +}; + +DLLSYM char tlabel[NUM_TEXT_ATTR][4][MAXLENGTH] = { { + //T_HORIZONTAL + "Horizontal", + "Vertical", + "Skew", + "" + }, + { //T_TEXT + "Text", + "Table", + "Form", + "Mixed" + }, + { //T_SERIF + "Serif", + "Sans-serif", + "", + "" + }, + { //T_PROPORTIONAL + "Proportional", + "Fixed pitch", + "", + "" + }, + { //T_NORMAL + "Normal", + "Bold", + "", + "" + }, + { //T_UPRIGHT + "Upright", + "Italic", + "", + "" + }, + { //T_SOLID + "Solid", + "Outline", + "", + "" + }, + { //T_BLACK + "Black", + "White", + "Coloured", + "" + }, + { //T_NOTUNDER + "Not underlined", + "Underlined", + "", + "" + }, + { //T_NOTDROP + "Not drop caps", + "Drop Caps", + "", + "" + } +}; + +DLLSYM INT32 bn[NUM_BLOCK_ATTR] = { + 4, //G_MONOCHROME + 2, //I_MONOCHROME + 2, //I_SMOOTH + 3, //R_SINGLE + 3, //R_BLACK + 3, //S_BLACK + 2 //W_TEXT +}; + +DLLSYM INT32 tvar[NUM_TEXT_ATTR]; +DLLSYM INT32 bvar[NUM_BLOCK_ATTR]; +DLLSYM char blabel[NUM_BLOCK_ATTR][4][MAXLENGTH] = { { + //G_MONOCHROME + + /**************************************************************************** + * GRAPHICS + ***************************************************************************/ + "Monochrome ", + "Two colour ", + "Spot colour", + "Multicolour" + }, + + /**************************************************************************** + * IMAGE + ***************************************************************************/ + { //I_MONOCHROME + "Monochrome ", + "Colour ", + "", + "" + }, + { //I_SMOOTH + "Smooth ", + "Grainy ", + "", + "" + }, + + /**************************************************************************** + * RULES + ***************************************************************************/ + { //R_SINGLE + "Single ", + "Double ", + "Multiple", + "" + }, + { //R_BLACK + "Black ", + "White ", + "Coloured", + "" + }, + + /**************************************************************************** + * SCRIBBLE + ***************************************************************************/ + { //S_BLACK + "Black ", + "White ", + "Coloured", + "" + }, + /**************************************************************************** + * WEIRD + ***************************************************************************/ + { //W_TEXT + "No text ", + "Contains text", + "", + "" + } +}; + +DLLSYM char backlabel[NUM_BACKGROUNDS][MAXLENGTH] = { + "White", //B_WHITE + "Black", //B_BLACK + "Coloured", //B_COLOURED + "Textured", //B_TEXTURED + "Patterned", //B_PATTERNED + "Gradient fill", //B_GRADIENTFILL + "Image", //B_IMAGE + "Text" //B_TEXT +}; diff --git a/ccstruct/labls.h b/ccstruct/labls.h new file mode 100644 index 0000000000..d55b9d3e0b --- /dev/null +++ b/ccstruct/labls.h @@ -0,0 +1,38 @@ +/********************************************************************** + * File: labls.h (Formerly labels.h) + * Description: Attribute definition tables + * Author: Sheelagh Lloyd? + * Created: + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#ifndef LABLS_H +#define LABLS_H + +#include "host.h" +#include "hpdsizes.h" + +#include "hpddef.h" //must be last (handpd.dll) + +extern DLLSYM INT32 tn[NUM_TEXT_ATTR]; + +extern DLLSYM char tlabel[NUM_TEXT_ATTR][4][MAXLENGTH]; + +extern DLLSYM INT32 bn[NUM_BLOCK_ATTR]; + +extern DLLSYM INT32 tvar[NUM_TEXT_ATTR]; +extern DLLSYM INT32 bvar[NUM_BLOCK_ATTR]; +extern DLLSYM char blabel[NUM_BLOCK_ATTR][4][MAXLENGTH]; + +extern DLLSYM char backlabel[NUM_BACKGROUNDS][MAXLENGTH]; +#endif diff --git a/ccstruct/linlsq.cpp b/ccstruct/linlsq.cpp new file mode 100644 index 0000000000..ad620909bd --- /dev/null +++ b/ccstruct/linlsq.cpp @@ -0,0 +1,249 @@ +/********************************************************************** + * File: linlsq.cpp (Formerly llsq.c) + * Description: Linear Least squares fitting code. + * Author: Ray Smith + * Created: Thu Sep 12 08:44:51 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include +#include "errcode.h" +#include "linlsq.h" + +#ifndef __UNIX__ +#define M_PI 3.14159265359 +#endif + +const ERRCODE EMPTY_LLSQ = "Can't delete from an empty LLSQ"; + +#define EXTERN + +EXTERN double_VAR (pdlsq_posdir_ratio, 4e-6, "Mult of dir to cf pos"); +EXTERN double_VAR (pdlsq_threshold_angleavg, 0.1666666, +"Frac of pi for simple fit"); + +/********************************************************************** + * LLSQ::clear + * + * Function to initialize a LLSQ. + **********************************************************************/ + +void LLSQ::clear() { //initialize + n = 0; //no elements + sigx = 0; //update accumulators + sigy = 0; + sigxx = 0; + sigxy = 0; + sigyy = 0; +} + + +/********************************************************************** + * LLSQ::add + * + * Add an element to the accumulator. + **********************************************************************/ + +void LLSQ::add( //add an element + double x, //xcoord + double y //ycoord + ) { + n++; //count elements + sigx += x; //update accumulators + sigy += y; + sigxx += x * x; + sigxy += x * y; + sigyy += y * y; +} + + +/********************************************************************** + * LLSQ::remove + * + * Delete an element from the acculuator. + **********************************************************************/ + +void LLSQ::remove( //delete an element + double x, //xcoord + double y //ycoord + ) { + if (n <= 0) + //illegal + EMPTY_LLSQ.error ("LLSQ::remove", ABORT, NULL); + n--; //count elements + sigx -= x; //update accumulators + sigy -= y; + sigxx -= x * x; + sigxy -= x * y; + sigyy -= y * y; +} + + +/********************************************************************** + * LLSQ::m + * + * Return the gradient of the line fit. + **********************************************************************/ + +double LLSQ::m() { //get gradient + if (n > 1) + return (sigxy - sigx * sigy / n) / (sigxx - sigx * sigx / n); + else + return 0; //too little +} + + +/********************************************************************** + * LLSQ::c + * + * Return the constant of the line fit. + **********************************************************************/ + +double LLSQ::c( //get constant + double m //gradient to fit with + ) { + if (n > 0) + return (sigy - m * sigx) / n; + else + return 0; //too little +} + + +/********************************************************************** + * LLSQ::rms + * + * Return the rms error of the fit. + **********************************************************************/ + +double LLSQ::rms( //get error + double m, //gradient to fit with + double c //constant to fit with + ) { + double error; //total error + + if (n > 0) { + error = + sigyy + m * (m * sigxx + 2 * (c * sigx - sigxy)) + c * (n * c - + 2 * sigy); + if (error >= 0) + error = sqrt (error / n); //sqrt of mean + else + error = 0; + } + else + error = 0; //too little + return error; +} + + +/********************************************************************** + * LLSQ::spearman + * + * Return the spearman correlation coefficient. + **********************************************************************/ + +double LLSQ::spearman() { //get error + double error; //total error + + if (n > 1) { + error = (sigxx - sigx * sigx / n) * (sigyy - sigy * sigy / n); + if (error > 0) { + error = (sigxy - sigx * sigy / n) / sqrt (error); + } + else + error = 1; + } + else + error = 1; //too little + return error; +} + + +/********************************************************************** + * PDLSQ::fit + * + * Return all the parameters of the fit to pos/dir. + * The return value is the rms error. + **********************************************************************/ + +float PDLSQ::fit( //get fit + DIR128 &ang, //output angle + float &sin_ang, //r,theta parameterisation + float &cos_ang, + float &r) { + double a, b; //itermediates + double angle; //resulting angle + double avg_angle; //simple average + double error; //total error + double sinx, cosx; //return values + + if (pos.n > 0) { + a = pos.sigxy - pos.sigx * pos.sigy / pos.n + + pdlsq_posdir_ratio * dir.sigxy; + b = + pos.sigxx - pos.sigyy + (pos.sigy * pos.sigy - + pos.sigx * pos.sigx) / pos.n + + pdlsq_posdir_ratio * (dir.sigxx - dir.sigyy); + if (dir.sigy != 0 || dir.sigx != 0) + avg_angle = atan2 (dir.sigy, dir.sigx); + else + avg_angle = 0; + if ((a != 0 || b != 0) && pos.n > 1) + angle = atan2 (2 * a, b) / 2; + else + angle = avg_angle; + error = avg_angle - angle; + if (error > M_PI / 2) { + error -= M_PI; + angle += M_PI; + } + if (error < -M_PI / 2) { + error += M_PI; + angle -= M_PI; + } + if (error > M_PI * pdlsq_threshold_angleavg + || error < -M_PI * pdlsq_threshold_angleavg) + angle = avg_angle; //go simple + //convert direction + ang = (INT16) (angle * MODULUS / (2 * M_PI)); + sinx = sin (angle); + cosx = cos (angle); + r = (sinx * pos.sigx - cosx * pos.sigy) / pos.n; + // tprintf("x=%g, y=%g, xx=%g, xy=%g, yy=%g, a=%g, b=%g, ang=%g, r=%g\n", + // pos.sigx,pos.sigy,pos.sigxx,pos.sigxy,pos.sigyy, + // a,b,angle,r); + error = dir.sigxx * sinx * sinx + dir.sigyy * cosx * cosx + - 2 * dir.sigxy * sinx * cosx; + error *= pdlsq_posdir_ratio; + error += sinx * sinx * pos.sigxx + cosx * cosx * pos.sigyy + - 2 * sinx * cosx * pos.sigxy + - 2 * r * (sinx * pos.sigx - cosx * pos.sigy) + r * r * pos.n; + if (error >= 0) + //rms value + error = sqrt (error / pos.n); + else + error = 0; //-0 + sin_ang = sinx; + cos_ang = cosx; + } + else { + sin_ang = 0.0f; + cos_ang = 0.0f; + ang = 0; + error = 0; //too little + } + return error; +} diff --git a/ccstruct/linlsq.h b/ccstruct/linlsq.h new file mode 100644 index 0000000000..3360935f14 --- /dev/null +++ b/ccstruct/linlsq.h @@ -0,0 +1,102 @@ +/********************************************************************** + * File: linlsq.h (Formerly llsq.h) + * Description: Linear Least squares fitting code. + * Author: Ray Smith + * Created: Thu Sep 12 08:44:51 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef LINLSQ_H +#define LINLSQ_H + +#include "points.h" +#include "mod128.h" +#include "varable.h" + +class LLSQ +{ + friend class PDLSQ; //pos & direction + + public: + LLSQ() { //constructor + clear(); //set to zeros + } + void clear(); //initialize + + void add( //add element + double x, //coords to add + double y); + void remove( //delete element + double x, //coords to delete + double y); + INT32 count() { //no of elements + return n; + } + + double m(); //get gradient + double c( //get constant + double m); //gradient + double rms( //get error + double m, //gradient + double c); //constant + double spearman(); //get error + + private: + INT32 n; //no of elements + double sigx; //sum of x + double sigy; //sum of y + double sigxx; //sum x squared + double sigxy; //sum of xy + double sigyy; //sum y squared +}; + +class PDLSQ +{ + public: + PDLSQ() { //constructor + clear(); //set to zeros + } + void clear() { //initialize + pos.clear (); //clear both + dir.clear (); + } + + void add( //add element + const ICOORD &addpos, //position of pt + const ICOORD &adddir) { //dir of pt + pos.add (addpos.x (), addpos.y ()); + dir.add (adddir.x (), adddir.y ()); + } + void remove( //remove element + const ICOORD &removepos, //position of pt + const ICOORD &removedir) { //dir of pt + pos.remove (removepos.x (), removepos.y ()); + dir.remove (removedir.x (), removedir.y ()); + } + INT32 count() { //no of elements + return pos.count (); + } + + float fit( //get fit parameters + DIR128 &ang, //output angle + float &sin_ang, //output components + float &cos_ang, + float &r); + + private: + LLSQ pos; //position + LLSQ dir; //directions +}; +extern double_VAR_H (pdlsq_posdir_ratio, 0.4e-6, "Mult of dir to cf pos"); +#endif diff --git a/ccstruct/lmedsq.cpp b/ccstruct/lmedsq.cpp new file mode 100644 index 0000000000..cc62fe17b9 --- /dev/null +++ b/ccstruct/lmedsq.cpp @@ -0,0 +1,453 @@ +/********************************************************************** + * File: lmedsq.cpp (Formerly lms.c) + * Description: Code for the LMS class. + * Author: Ray Smith + * Created: Fri Aug 7 09:30:53 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include "statistc.h" +#include "memry.h" +#include "statistc.h" +#include "lmedsq.h" + +#define EXTERN + +EXTERN INT_VAR (lms_line_trials, 12, "Number of linew fits to do"); +#define SEED1 0x1234 //default seeds +#define SEED2 0x5678 +#define SEED3 0x9abc +#define LMS_MAX_FAILURES 3 + +#ifndef __UNIX__ +UINT32 nrand48( //get random number + UINT16 *seeds //seeds to use + ) { + static UINT32 seed = 0; //only seed + + if (seed == 0) { + seed = seeds[0] ^ (seeds[1] << 8) ^ (seeds[2] << 16); + srand(seed); + } + //make 32 bit one + return rand () | (rand () << 16); +} +#endif + +/********************************************************************** + * LMS::LMS + * + * Construct a LMS class, given the max no of samples to be given + **********************************************************************/ + +LMS::LMS ( //constructor +INT32 size //samplesize +):samplesize (size) { + samplecount = 0; + a = 0; + m = 0.0f; + c = 0.0f; + samples = (FCOORD *) alloc_mem (size * sizeof (FCOORD)); + errors = (float *) alloc_mem (size * sizeof (float)); + line_error = 0.0f; + fitted = FALSE; +} + + +/********************************************************************** + * LMS::~LMS + * + * Destruct a LMS class. + **********************************************************************/ + +LMS::~LMS ( //constructor +) { + free_mem(samples); + free_mem(errors); +} + + +/********************************************************************** + * LMS::clear + * + * Clear samples from array. + **********************************************************************/ + +void LMS::clear() { //clear sample + samplecount = 0; + fitted = FALSE; +} + + +/********************************************************************** + * LMS::add + * + * Add another sample. More than the constructed number will be ignored. + **********************************************************************/ + +void LMS::add( //add sample + FCOORD sample //sample coords + ) { + if (samplecount < samplesize) + //save it + samples[samplecount++] = sample; + fitted = FALSE; +} + + +/********************************************************************** + * LMS::fit + * + * Fit a line to the given sample points. + **********************************************************************/ + +void LMS::fit( //fit sample + float &out_m, //output line + float &out_c) { + INT32 index; //of median + INT32 trials; //no of medians + float test_m, test_c; //candidate line + float test_error; //error of test line + + switch (samplecount) { + case 0: + m = 0.0f; //no info + c = 0.0f; + line_error = 0.0f; + break; + + case 1: + m = 0.0f; + c = samples[0].y (); //horiz thru pt + line_error = 0.0f; + break; + + case 2: + if (samples[0].x () != samples[1].x ()) { + m = (samples[1].y () - samples[0].y ()) + / (samples[1].x () - samples[0].x ()); + c = samples[0].y () - m * samples[0].x (); + } + else { + m = 0.0f; + c = (samples[0].y () + samples[1].y ()) / 2; + } + line_error = 0.0f; + break; + + default: + pick_line(m, c); //use pts at random + compute_errors(m, c); //from given line + index = choose_nth_item (samplecount / 2, errors, samplecount); + line_error = errors[index]; + for (trials = 1; trials < lms_line_trials; trials++) { + //random again + pick_line(test_m, test_c); + compute_errors(test_m, test_c); + index = choose_nth_item (samplecount / 2, errors, samplecount); + test_error = errors[index]; + if (test_error < line_error) { + //find least median + line_error = test_error; + m = test_m; + c = test_c; + } + } + } + fitted = TRUE; + out_m = m; + out_c = c; + a = 0; +} + + +/********************************************************************** + * LMS::fit_quadratic + * + * Fit a quadratic to the given sample points. + **********************************************************************/ + +void LMS::fit_quadratic( //fit sample + float outlier_threshold, //min outlier size + double &out_a, //x squared + float &out_b, //output line + float &out_c) { + INT32 trials; //no of medians + double test_a; + float test_b, test_c; //candidate line + float test_error; //error of test line + + if (samplecount < 3) { + out_a = 0; + fit(out_b, out_c); + return; + } + pick_quadratic(a, m, c); + line_error = compute_quadratic_errors (outlier_threshold, a, m, c); + for (trials = 1; trials < lms_line_trials * 2; trials++) { + pick_quadratic(test_a, test_b, test_c); + test_error = compute_quadratic_errors (outlier_threshold, + test_a, test_b, test_c); + if (test_error < line_error) { + line_error = test_error; //find least median + a = test_a; + m = test_b; + c = test_c; + } + } + fitted = TRUE; + out_a = a; + out_b = m; + out_c = c; +} + + +/********************************************************************** + * LMS::constrained_fit + * + * Fit a line to the given sample points. + * The line must have the given gradient. + **********************************************************************/ + +void LMS::constrained_fit( //fit sample + float fixed_m, //forced gradient + float &out_c) { + INT32 index; //of median + INT32 trials; //no of medians + float test_c; //candidate line + static UINT16 seeds[3] = { SEED1, SEED2, SEED3 }; + //for nrand + float test_error; //error of test line + + m = fixed_m; + switch (samplecount) { + case 0: + c = 0.0f; + line_error = 0.0f; + break; + + case 1: + //horiz thru pt + c = samples[0].y () - m * samples[0].x (); + line_error = 0.0f; + break; + + case 2: + c = (samples[0].y () + samples[1].y () + - m * (samples[0].x () + samples[1].x ())) / 2; + line_error = m * samples[0].x () + c - samples[0].y (); + line_error *= line_error; + break; + + default: + index = (INT32) nrand48 (seeds) % samplecount; + //compute line + c = samples[index].y () - m * samples[index].x (); + compute_errors(m, c); //from given line + index = choose_nth_item (samplecount / 2, errors, samplecount); + line_error = errors[index]; + for (trials = 1; trials < lms_line_trials; trials++) { + index = (INT32) nrand48 (seeds) % samplecount; + test_c = samples[index].y () - m * samples[index].x (); + //compute line + compute_errors(m, test_c); + index = choose_nth_item (samplecount / 2, errors, samplecount); + test_error = errors[index]; + if (test_error < line_error) { + //find least median + line_error = test_error; + c = test_c; + } + } + } + fitted = TRUE; + out_c = c; + a = 0; +} + + +/********************************************************************** + * LMS::pick_line + * + * Fit a line to a random pair of sample points. + **********************************************************************/ + +void LMS::pick_line( //fit sample + float &line_m, //output gradient + float &line_c) { + INT16 trial_count; //no of attempts + static UINT16 seeds[3] = { SEED1, SEED2, SEED3 }; + //for nrand + INT32 index1; //picked point + INT32 index2; //picked point + + trial_count = 0; + do { + index1 = (INT32) nrand48 (seeds) % samplecount; + index2 = (INT32) nrand48 (seeds) % samplecount; + line_m = samples[index2].x () - samples[index1].x (); + trial_count++; + } + while (line_m == 0 && trial_count < LMS_MAX_FAILURES); + if (line_m == 0) { + line_c = (samples[index2].y () + samples[index1].y ()) / 2; + } + else { + line_m = (samples[index2].y () - samples[index1].y ()) / line_m; + line_c = samples[index1].y () - samples[index1].x () * line_m; + } +} + + +/********************************************************************** + * LMS::pick_quadratic + * + * Fit a quadratic to a random triplet of sample points. + **********************************************************************/ + +void LMS::pick_quadratic( //fit sample + double &line_a, //x suaread + float &line_m, //output gradient + float &line_c) { + INT16 trial_count; //no of attempts + static UINT16 seeds[3] = { SEED1, SEED2, SEED3 }; + //for nrand + INT32 index1; //picked point + INT32 index2; //picked point + INT32 index3; + FCOORD x1x2; //vector + FCOORD x1x3; + FCOORD x3x2; + double bottom; //of a + + trial_count = 0; + do { + if (trial_count >= LMS_MAX_FAILURES - 1) { + index1 = 0; + index2 = samplecount / 2; + index3 = samplecount - 1; + } + else { + index1 = (INT32) nrand48 (seeds) % samplecount; + index2 = (INT32) nrand48 (seeds) % samplecount; + index3 = (INT32) nrand48 (seeds) % samplecount; + } + x1x2 = samples[index2] - samples[index1]; + x1x3 = samples[index3] - samples[index1]; + x3x2 = samples[index2] - samples[index3]; + bottom = x1x2.x () * x1x3.x () * x3x2.x (); + trial_count++; + } + while (bottom == 0 && trial_count < LMS_MAX_FAILURES); + if (bottom == 0) { + line_a = 0; + pick_line(line_m, line_c); + } + else { + line_a = x1x3 * x1x2 / bottom; + line_m = x1x2.y () - line_a * x1x2.x () + * (samples[index2].x () + samples[index1].x ()); + line_m /= x1x2.x (); + line_c = samples[index1].y () - samples[index1].x () + * (samples[index1].x () * line_a + line_m); + } +} + + +/********************************************************************** + * LMS::compute_errors + * + * Compute the squared error from all the points. + **********************************************************************/ + +void LMS::compute_errors( //fit sample + float line_m, //input gradient + float line_c) { + INT32 index; //picked point + + for (index = 0; index < samplecount; index++) { + errors[index] = + line_m * samples[index].x () + line_c - samples[index].y (); + errors[index] *= errors[index]; + } +} + + +/********************************************************************** + * LMS::compute_quadratic_errors + * + * Compute the squared error from all the points. + **********************************************************************/ + +float LMS::compute_quadratic_errors( //fit sample + float outlier_threshold, //min outlier + double line_a, + float line_m, //input gradient + float line_c) { + INT32 outlier_count; //total outliers + INT32 index; //picked point + INT32 error_count; //no in total + double total_error; //summed squares + + total_error = 0; + outlier_count = 0; + error_count = 0; + for (index = 0; index < samplecount; index++) { + errors[error_count] = line_c + samples[index].x () + * (line_m + samples[index].x () * line_a) - samples[index].y (); + errors[error_count] *= errors[error_count]; + if (errors[error_count] > outlier_threshold) { + outlier_count++; + errors[samplecount - outlier_count] = errors[error_count]; + } + else { + total_error += errors[error_count++]; + } + } + if (outlier_count * 3 < error_count) + return total_error / error_count; + else { + index = choose_nth_item (outlier_count / 2, + errors + samplecount - outlier_count, + outlier_count); + //median outlier + return errors[samplecount - outlier_count + index]; + } +} + + +/********************************************************************** + * LMS::plot + * + * Plot the fitted line of a LMS. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void LMS::plot( //plot fit + WINDOW win, //window + COLOUR colour //colour to draw in + ) { + if (fitted) { + line_color_index(win, colour); + move2d (win, samples[0].x (), + c + samples[0].x () * (m + samples[0].x () * a)); + draw2d (win, samples[samplecount - 1].x (), + c + samples[samplecount - 1].x () * (m + + samples[samplecount - + 1].x () * a)); + } +} +#endif diff --git a/ccstruct/lmedsq.h b/ccstruct/lmedsq.h new file mode 100644 index 0000000000..39294b0cc0 --- /dev/null +++ b/ccstruct/lmedsq.h @@ -0,0 +1,84 @@ +/********************************************************************** + * File: lmedsq.h (Formerly lms.h) + * Description: Code for the LMS class. + * Author: Ray Smith + * Created: Fri Aug 7 09:30:53 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef LMEDSQ_H +#define LMEDSQ_H + +#include "points.h" +#include "varable.h" +#include "grphics.h" +#include "notdll.h" + +class LMS +{ + public: + LMS( //constructor + INT32 size); //no of samples + ~LMS (); //destructor + void clear(); //clear samples + void add( //add sample + FCOORD sample); //sample coords + void fit( //generate fit + float &m, //output line + float &c); + void constrained_fit( //fixed gradient + float fixed_m, //forced gradient + float &out_c); //output line + void fit_quadratic( //easy quadratic + float outlier_threshold, //min outlier + double &a, //x squared + float &b, //x + float &c); //constant + void plot( //plot fit + WINDOW win, //window + COLOUR colour); //colour to draw in + float error() { //get error + return fitted ? line_error : -1; + } + + private: + + void pick_line( //random choice + float &m, //output line + float &c); + void pick_quadratic( //random choice + double &a, //output curve + float &b, + float &c); + void compute_errors( //find errors + float m, //from line + float c); + //find errors + float compute_quadratic_errors(float outlier_threshold, //min outlier + double a, //from curve + float m, + float c); + + BOOL8 fitted; //line parts valid + INT32 samplesize; //max samples + INT32 samplecount; //current sample size + FCOORD *samples; //array of samples + float *errors; //error distances + double a; //x squared + float m; //line gradient + float c; + float line_error; //error of fit +}; +extern INT_VAR_H (lms_line_trials, 12, "Number of linew fits to do"); +#endif diff --git a/ccstruct/mod128.cpp b/ccstruct/mod128.cpp new file mode 100644 index 0000000000..65c18c5cc6 --- /dev/null +++ b/ccstruct/mod128.cpp @@ -0,0 +1,100 @@ +/********************************************************************** + * File: mod128.c (Formerly dir128.c) + * Description: Code to convert a DIR128 to an ICOORD. + * Author: Ray Smith + * Created: Tue Oct 22 11:56:09 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include "mod128.h" + +static INT16 idirtab[] = { + 1000, 0, 998, 49, 995, 98, 989, 146, + 980, 195, 970, 242, 956, 290, 941, 336, + 923, 382, 903, 427, 881, 471, 857, 514, + 831, 555, 803, 595, 773, 634, 740, 671, + 707, 707, 671, 740, 634, 773, 595, 803, + 555, 831, 514, 857, 471, 881, 427, 903, + 382, 923, 336, 941, 290, 956, 242, 970, + 195, 980, 146, 989, 98, 995, 49, 998, + 0, 1000, -49, 998, -98, 995, -146, 989, + -195, 980, -242, 970, -290, 956, -336, 941, + -382, 923, -427, 903, -471, 881, -514, 857, + -555, 831, -595, 803, -634, 773, -671, 740, + -707, 707, -740, 671, -773, 634, -803, 595, + -831, 555, -857, 514, -881, 471, -903, 427, + -923, 382, -941, 336, -956, 290, -970, 242, + -980, 195, -989, 146, -995, 98, -998, 49, + -1000, 0, -998, -49, -995, -98, -989, -146, + -980, -195, -970, -242, -956, -290, -941, -336, + -923, -382, -903, -427, -881, -471, -857, -514, + -831, -555, -803, -595, -773, -634, -740, -671, + -707, -707, -671, -740, -634, -773, -595, -803, + -555, -831, -514, -857, -471, -881, -427, -903, + -382, -923, -336, -941, -290, -956, -242, -970, + -195, -980, -146, -989, -98, -995, -49, -998, + 0, -1000, 49, -998, 98, -995, 146, -989, + 195, -980, 242, -970, 290, -956, 336, -941, + 382, -923, 427, -903, 471, -881, 514, -857, + 555, -831, 595, -803, 634, -773, 671, -740, + 707, -707, 740, -671, 773, -634, 803, -595, + 831, -555, 857, -514, 881, -471, 903, -427, + 923, -382, 941, -336, 956, -290, 970, -242, + 980, -195, 989, -146, 995, -98, 998, -49 +}; + +static ICOORD *dirtab = (ICOORD *) idirtab; + +/********************************************************************** + * DIR128::DIR128 + * + * Quantize the direction of an FCOORD to make a DIR128. + **********************************************************************/ + +DIR128::DIR128( //from fcoord + const FCOORD fc //vector to quantize + ) { + int high, low, current; //binary search + + low = 0; + if (fc.y () == 0) { + if (fc.x () >= 0) + dir = 0; + else + dir = MODULUS / 2; + return; + } + high = MODULUS; + do { + current = (high + low) / 2; + if (dirtab[current] * fc >= 0) + low = current; + else + high = current; + } + while (high - low > 1); + dir = low; +} + + +/********************************************************************** + * dir_to_gradient + * + * Convert a direction to a vector. + **********************************************************************/ + +ICOORD DIR128::vector() const { //convert to vector + return dirtab[dir]; //easy really +} diff --git a/ccstruct/mod128.h b/ccstruct/mod128.h new file mode 100644 index 0000000000..38937319ff --- /dev/null +++ b/ccstruct/mod128.h @@ -0,0 +1,85 @@ +/********************************************************************** + * File: mod128.h (Formerly dir128.h) + * Description: Header for class which implements modulo arithmetic. + * Author: Ray Smith + * Created: Tue Mar 26 17:48:13 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef MOD128_H +#define MOD128_H + +#include "points.h" + +#define MODULUS 128 /*range of directions */ +#define DIRBITS 7 //no of bits used +#define DIRSCALE 1000 //length of vector + +class DLLSYM DIR128 +{ + public: + DIR128() { + } //empty constructor + + DIR128( //constructor + INT16 value) { //value to assign + value %= MODULUS; //modulo arithmetic + if (value < 0) + value += MODULUS; //done properly + dir = (INT8) value; + } + DIR128(const FCOORD fc); //quantize vector + + DIR128 & operator= ( //assign of INT16 + INT16 value) { //value to assign + value %= MODULUS; //modulo arithmetic + if (value < 0) + value += MODULUS; //done properly + dir = (INT8) value; + return *this; + } + INT8 operator- ( //subtraction + const DIR128 & minus) const//for signed result + { + //result + INT16 result = dir - minus.dir; + + if (result > MODULUS / 2) + result -= MODULUS; //get in range + else if (result < -MODULUS / 2) + result += MODULUS; + return (INT8) result; + } + DIR128 operator+ ( //addition + const DIR128 & add) const //of itself + { + DIR128 result; //sum + + result = dir + add.dir; //let = do the work + return result; + } + DIR128 & operator+= ( //same as + + const DIR128 & add) { + *this = dir + add.dir; //let = do the work + return *this; + } + INT8 get_dir() const { //access function + return dir; + } + ICOORD vector() const; //turn to vector + + private: + INT8 dir; //a direction +}; +#endif diff --git a/ccstruct/normalis.cpp b/ccstruct/normalis.cpp new file mode 100644 index 0000000000..d9fbdef07b --- /dev/null +++ b/ccstruct/normalis.cpp @@ -0,0 +1,176 @@ +/********************************************************************** + * File: normalis.cpp (Formerly denorm.c) + * Description: Code for the DENORM class. + * Author: Ray Smith + * Created: Thu Apr 23 09:22:43 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "werd.h" +#include "normalis.h" + +/********************************************************************** + * DENORM::binary_search_segment + * + * Find the segment to use for the given x. + **********************************************************************/ + +const DENORM_SEG *DENORM::binary_search_segment(float src_x) const { + int bottom, top, middle; //binary search + + bottom = 0; + top = segments; + do { + middle = (bottom + top) / 2; + if (segs[middle].xstart > src_x) + top = middle; + else + bottom = middle; + } + while (top - bottom > 1); + return &segs[bottom]; +} + +/********************************************************************** + * DENORM::scale_at_x + * + * Return scaling at a given (normalized) x coord. + **********************************************************************/ + +float DENORM::scale_at_x(float src_x) const { // In normalized coords. + if (segments != 0) { + const DENORM_SEG* seg = binary_search_segment(src_x); + if (seg->scale_factor > 0.0) + return seg->scale_factor; + } + return scale_factor; +} + +/********************************************************************** + * DENORM::yshift_at_x + * + * Return yshift at a given (normalized) x coord. + **********************************************************************/ + +float DENORM::yshift_at_x(float src_x) const { // In normalized coords. + if (segments != 0) { + const DENORM_SEG* seg = binary_search_segment(src_x); + if (seg->ycoord == -MAX_INT32) { + if (base_is_row) + return source_row->base_line(x(src_x)/scale_at_x(src_x) + x_centre); + else + return m * x(src_x) + c; + } else { + return seg->ycoord; + } + } + return source_row->base_line (x(src_x)/scale_at_x(src_x) + x_centre); +} + +/********************************************************************** + * DENORM::x + * + * Denormalise an x coordinate. + **********************************************************************/ + +float DENORM::x( //convert x coord + float src_x //coord to convert + ) const { + return src_x / scale_at_x(src_x) + x_centre; +} + + +/********************************************************************** + * DENORM::y + * + * Denormalise a y coordinate. + **********************************************************************/ + +float DENORM::y( //convert y coord + float src_y, //coord to convert + float src_centre //x location for base + ) const { + return (src_y - bln_baseline_offset) / scale_at_x(src_centre) + + yshift_at_x(src_centre); +} + + +DENORM::DENORM(float x, //from same pieces + float scaling, + double line_m, //default line + double line_c, + INT16 seg_count, //no of segments + DENORM_SEG *seg_pts, //actual segments + BOOL8 using_row, //as baseline + ROW *src) { + x_centre = x; //just copy + scale_factor = scaling; + source_row = src; + if (seg_count > 0) { + segs = new DENORM_SEG[seg_count]; + for (segments = 0; segments < seg_count; segments++) { + // It is possible, if infrequent that the segments may be out of order. + // since we are searching with a binary search, keep them in order. + if (segments == 0 || segs[segments - 1].xstart <= + seg_pts[segments].xstart) { + segs[segments] = seg_pts[segments]; + } else { + int i; + for (i = 0; i < segments + && segs[segments - 1 - i].xstart > seg_pts[segments].xstart; + ++i) { + segs[segments - i ] = segs[segments - 1 - i]; + } + segs[segments - i] = seg_pts[segments]; + } + } + } + else { + segments = 0; + segs = NULL; + } + base_is_row = using_row; + m = line_m; + c = line_c; +} + + +DENORM::DENORM(const DENORM &src) { + segments = 0; + segs = NULL; + *this = src; +} + + +DENORM & DENORM::operator= (const DENORM & src) { + x_centre = src.x_centre; + scale_factor = src.scale_factor; + source_row = src.source_row; + if (segments > 0) + delete[]segs; + if (src.segments > 0) { + segs = new DENORM_SEG[src.segments]; + for (segments = 0; segments < src.segments; segments++) + segs[segments] = src.segs[segments]; + } + else { + segments = 0; + segs = NULL; + } + base_is_row = src.base_is_row; + m = src.m; + c = src.c; + return *this; +} diff --git a/ccstruct/normalis.h b/ccstruct/normalis.h new file mode 100644 index 0000000000..a0d2812eb0 --- /dev/null +++ b/ccstruct/normalis.h @@ -0,0 +1,108 @@ +/********************************************************************** + * File: normalis.h (Formerly denorm.h) + * Description: Code for the DENORM class. + * Author: Ray Smith + * Created: Thu Apr 23 09:22:43 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef NORMALIS_H +#define NORMALIS_H + +#include + +class ROW; //forward decl + +class DENORM_SEG +{ + public: + DENORM_SEG() { + } //empty + + INT32 xstart; //start of segment + INT32 ycoord; //y at segment + float scale_factor; //for this segment +}; + +class DENORM +{ + public: + DENORM() { //constructor + source_row = NULL; + x_centre = 0.0f; + scale_factor = 1.0f; + segments = 0; + segs = NULL; + base_is_row = TRUE; + m = c = 0; + } + DENORM( //constructor + float x, //from same pieces + float scaling, + ROW *src) { + x_centre = x; //just copy + scale_factor = scaling; + source_row = src; + segments = 0; + segs = NULL; + base_is_row = TRUE; + m = c = 0; + } + DENORM( //constructor + float x, //from same pieces + float scaling, + double line_m, //default line //no of segments + double line_c, + INT16 seg_count, + DENORM_SEG *seg_pts, //actual segments + BOOL8 using_row, //as baseline + ROW *src); + DENORM(const DENORM &); + DENORM & operator= (const DENORM &); + ~DENORM () { + if (segments > 0) + delete[]segs; + } + + float origin() const { //get x centre + return x_centre; + } + float scale() const { //get scale + return scale_factor; + } + ROW *row() const { //get row + return source_row; + } + float x( //convert an xcoord + float src_x) const; + float y( //convert a ycoord + float src_y, //coord to convert + float src_centre) const; //normed x centre + float scale_at_x( // Return scaling at this coord. + float src_x) const; + float yshift_at_x( // Return yshift at this coord. + float src_x) const; + + private: + const DENORM_SEG *binary_search_segment(float src_x) const; + + BOOL8 base_is_row; //using row baseline? + INT16 segments; //no of segments + double c, m; //baseline + float x_centre; //middle of word + float scale_factor; //scaling + ROW *source_row; //row it came from + DENORM_SEG *segs; //array of segments +}; +#endif diff --git a/ccstruct/ocrblock.cpp b/ccstruct/ocrblock.cpp new file mode 100644 index 0000000000..103df51306 --- /dev/null +++ b/ccstruct/ocrblock.cpp @@ -0,0 +1,368 @@ +/********************************************************************** + * File: ocrblock.cpp (Formerly block.c) + * Description: BLOCK member functions and iterator functions. + * Author: Ray Smith + * Created: Fri Mar 15 09:41:28 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include "blckerr.h" +#include "ocrblock.h" +#include "tprintf.h" + +#define BLOCK_LABEL_HEIGHT 150 //char height of block id + +ELISTIZE_S (BLOCK) +/********************************************************************** + * BLOCK::BLOCK + * + * Constructor for a simple rectangular block. + **********************************************************************/ +BLOCK::BLOCK ( //rectangular block +const char *name, //filename +BOOL8 prop, //proportional +INT16 kern, //kerning +INT16 space, //spacing +INT16 xmin, //bottom left +INT16 ymin, INT16 xmax, //top right +INT16 ymax): +PDBLK (xmin, ymin, xmax, ymax), +filename(name) { //box(ICOORD(xmin,ymin),ICOORD(xmax,ymax)) + //boundaries + ICOORDELT_IT left_it = &leftside; + ICOORDELT_IT right_it = &rightside; + + proportional = prop; + kerning = kern; + spacing = space; + font_class = -1; //not assigned + hand_block = NULL; + hand_poly = NULL; + left_it.set_to_list (&leftside); + right_it.set_to_list (&rightside); + //make default box + left_it.add_to_end (new ICOORDELT (xmin, ymin)); + left_it.add_to_end (new ICOORDELT (xmin, ymax)); + right_it.add_to_end (new ICOORDELT (xmax, ymin)); + right_it.add_to_end (new ICOORDELT (xmax, ymax)); +} + + +/********************************************************************** + * BLOCK::set_sides + * + * Sets left and right vertex lists + **********************************************************************/ + +//void BLOCK::set_sides( //set vertex lists +//ICOORDELT_LIST *left, //left vertices +//ICOORDELT_LIST *right //right vertices +//) +//{ +// ICOORDELT_IT left_it= &leftside; //boundaries +// ICOORDELT_IT right_it= &rightside; + +// leftside.clear(); +// left_it.move_to_first(); +// left_it.add_list_before(left); +// rightside.clear(); +// right_it.move_to_first(); +// right_it.add_list_before(right); +//} + +/********************************************************************** + * BLOCK::contains + * + * Return TRUE if the given point is within the block. + **********************************************************************/ + +//BOOL8 BLOCK::contains( //test containment +//ICOORD pt //point to test +//) +//{ +// BLOCK_RECT_IT it=this; //rectangle iterator +// ICOORD bleft,tright; //corners of rectangle + +// for (it.start_block();!it.cycled_rects();it.forward()) +// { +// it.bounding_box(bleft,tright); //get rectangle +// if (pt.x()>=bleft.x() && pt.x()<=tright.x() //inside rect +// && pt.y()>=bleft.y() && pt.y()<=tright.y()) +// return TRUE; //is inside +// } +// return FALSE; //not inside +//} + +/********************************************************************** + * BLOCK::move + * + * Reposition block + **********************************************************************/ + +//void BLOCK::move( // reposition block +//const ICOORD vec // by vector +//) +//{ +// ROW_IT row_it( &rows ); +// ICOORDELT_IT it( &leftside ); + +// for( row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward() ) +// row_it.data()->move( vec ); + +// for( it.mark_cycle_pt(); !it.cycled_list(); it.forward() ) +// *(it.data()) += vec; + +// it.set_to_list( &rightside ); + +// for( it.mark_cycle_pt(); !it.cycled_list(); it.forward() ) +// *(it.data()) += vec; + +// box.move( vec ); +//} + +/********************************************************************** + * decreasing_top_order + * + * Sort Comparator: Return <0 if row1 top < row2 top + **********************************************************************/ + +int decreasing_top_order( // + const void *row1, + const void *row2) { + return (*(ROW **) row2)->bounding_box ().top () - + (*(ROW **) row1)->bounding_box ().top (); +} + + +/********************************************************************** + * BLOCK::sort_rows + * + * Order rows so that they are in order of decreasing Y coordinate + **********************************************************************/ + +void BLOCK::sort_rows() { // order on "top" + ROW_IT row_it(&rows); + + row_it.sort (decreasing_top_order); +} + + +/********************************************************************** + * BLOCK::compress + * + * Delete space between the rows. (And maybe one day, compress the rows) + * Fill space of block from top down, left aligning rows. + **********************************************************************/ + +void BLOCK::compress() { // squash it up + #define ROW_SPACING 5 + + ROW_IT row_it(&rows); + ROW *row; + ICOORD row_spacing (0, ROW_SPACING); + + ICOORDELT_IT icoordelt_it; + + sort_rows(); + + box = BOX (box.topleft (), box.topleft ()); + box.move_bottom_edge (ROW_SPACING); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + row->move (box.botleft () - row_spacing - + row->bounding_box ().topleft ()); + box += row->bounding_box (); + } + + leftside.clear (); + icoordelt_it.set_to_list (&leftside); + icoordelt_it.add_to_end (new ICOORDELT (box.left (), box.bottom ())); + icoordelt_it.add_to_end (new ICOORDELT (box.left (), box.top ())); + rightside.clear (); + icoordelt_it.set_to_list (&rightside); + icoordelt_it.add_to_end (new ICOORDELT (box.right (), box.bottom ())); + icoordelt_it.add_to_end (new ICOORDELT (box.right (), box.top ())); +} + + +/********************************************************************** + * BLOCK::check_pitch + * + * Check whether the block is fixed or prop, set the flag, and set + * the pitch if it is fixed. + **********************************************************************/ + +void BLOCK::check_pitch() { // check prop + // tprintf("Missing FFT fixed pitch stuff!\n"); + pitch = -1; +} + + +/********************************************************************** + * BLOCK::compress + * + * Compress and move in a single operation. + **********************************************************************/ + +void BLOCK::compress( // squash it up + const ICOORD vec // and move + ) { + box.move (vec); + compress(); +} + + +/********************************************************************** + * BLOCK::print + * + * Print the info on a block + **********************************************************************/ + +void BLOCK::print( //print list of sides + FILE *, //file to print on + BOOL8 dump //print full detail + ) { + ICOORDELT_IT it = &leftside; //iterator + + box.print (); + tprintf ("Proportional= %s\n", proportional ? "TRUE" : "FALSE"); + tprintf ("Kerning= %d\n", kerning); + tprintf ("Spacing= %d\n", spacing); + tprintf ("Fixed_pitch=%d\n", pitch); + tprintf ("Filename= %s\n", filename.string ()); + + if (dump) { + tprintf ("Left side coords are:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + tprintf ("(%d,%d) ", it.data ()->x (), it.data ()->y ()); + tprintf ("\n"); + tprintf ("Right side coords are:\n"); + it.set_to_list (&rightside); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + tprintf ("(%d,%d) ", it.data ()->x (), it.data ()->y ()); + tprintf ("\n"); + } +} + + +/********************************************************************** + * BLOCK::plot + * + * Plot the outline of a block in the given colour. + **********************************************************************/ + +//void BLOCK::plot( //draw outline +//WINDOW window, //window to draw in +//INT32 serial, //serial number +//COLOUR colour //colour to draw in +//) +//{ +// ICOORD startpt; //start of outline +// ICOORD endpt; //end of outline +// ICOORD prevpt; //previous point +// ICOORDELT_IT it= &leftside; //iterator +// char number[32]; //block id + +// line_color_index(window,colour); //set the colour +// text_color_index(window,colour); +// character_height(window,(float)BLOCK_LABEL_HEIGHT); +// text_font_index(window,6); + +// if (!leftside.empty()) +// { +// startpt= *(it.data()); //bottom left corner +//// fprintf(stderr,"Block %d bottom left is (%d,%d)\n", +//// serial,startpt.x(),startpt.y()); +// sprintf(number,"%d",serial); +// text2d(window,startpt.x(),startpt.y(),number,0,FALSE); + +// move2d(window,startpt.x(),startpt.y()); +// do +// { +// prevpt= *(it.data()); //previous point +// it.forward(); //move to next point +// draw2d(window,prevpt.x(),it.data()->y()); //draw round corner +// draw2d(window,it.data()->x(),it.data()->y()); +// } +// while (!it.at_last()); //until end of list +// endpt= *(it.data()); //end point + +// move2d(window,startpt.x(),startpt.y()); //other side of boundary +// it.set_to_list(&rightside); +// prevpt=startpt; +// for (it.mark_cycle_pt();!it.cycled_list();it.forward()) +// { +// draw2d(window,prevpt.x(),it.data()->y()); //draw round corner +// draw2d(window,it.data()->x(),it.data()->y()); +// prevpt= *(it.data()); //previous point +// } +// draw2d(window,endpt.x(),endpt.y()); //close boundary +// if (hand_block!=NULL) +// hand_block->plot(window,colour,serial); +// } +//} + +/********************************************************************** + * BLOCK::show + * + * Show the image corresponding to a block as its set of rectangles. + **********************************************************************/ + +//void BLOCK::show( //show image block +//IMAGE *image, //image to show +//WINDOW window //window to show in +//) +//{ +// BLOCK_RECT_IT it=this; //rectangle iterator +// ICOORD bleft,tright; //corners of rectangle + +// for (it.start_block();!it.cycled_rects();it.forward()) +// { +// it.bounding_box(bleft,tright); //get rectangle +//// fprintf(stderr,"Drawing a block with a bottom left of (%d,%d)\n", +//// bleft.x(),bleft.y()); +// show_sub_image(image,bleft.x(),bleft.y(), +// tright.x()-bleft.x(),tright.y()-bleft.y(), +// window,bleft.x(),bleft.y()); //show it +// } +//} + +/********************************************************************** + * BLOCK::operator= + * + * Assignment - duplicate the block structure, but with an EMPTY row list. + **********************************************************************/ + +BLOCK & BLOCK::operator= ( //assignment +const BLOCK & source //from this +) { + this->ELIST_LINK::operator= (source); + this->PDBLK::operator= (source); + proportional = source.proportional; + kerning = source.kerning; + spacing = source.spacing; + filename = source.filename; //STRINGs assign ok + if (!rows.empty ()) + rows.clear (); + // if ( !leftside.empty() ) + // leftside.clear(); + // if ( !rightside.empty() ) + // rightside.clear(); + // leftside.deep_copy( &source.leftside ); + // rightside.deep_copy( &source.rightside ); + // box=source.box; + return *this; +} diff --git a/ccstruct/ocrblock.h b/ccstruct/ocrblock.h new file mode 100644 index 0000000000..e8ae377200 --- /dev/null +++ b/ccstruct/ocrblock.h @@ -0,0 +1,228 @@ +/********************************************************************** + * File: ocrblock.h (Formerly block.h) + * Description: Page block class definition. + * Author: Ray Smith + * Created: Thu Mar 14 17:32:01 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef OCRBLOCK_H +#define OCRBLOCK_H + +#include "img.h" +#include "ocrrow.h" +#include "pageblk.h" +#include "pdblock.h" + +class BLOCK; //forward decl + +ELISTIZEH_S (BLOCK) +class BLOCK:public ELIST_LINK, public PDBLK +//page block +{ + friend class BLOCK_RECT_IT; //block iterator + + //block label + friend void scan_hpd_blocks(const char *name, + PAGE_BLOCK_LIST *page_blocks, //head of full pag + INT32 &block_no, //no of blocks + BLOCK_IT *block_it); + friend BOOL8 read_vec_file( //read uscan output + STRING name, //basename of file + INT32 xsize, //page size //output list + INT32 ysize, + BLOCK_LIST *blocks); + friend BOOL8 read_pd_file( //read uscan output + STRING name, //basename of file + INT32 xsize, //page size //output list + INT32 ysize, + BLOCK_LIST *blocks); + + public: + BLOCK() { //empty constructor + hand_block = NULL; + hand_poly = NULL; + } + BLOCK( //simple constructor + const char *name, //filename + BOOL8 prop, //proportional + INT16 kern, //kerning + INT16 space, //spacing + INT16 xmin, //bottom left + INT16 ymin, + INT16 xmax, //top right + INT16 ymax); + + // void set_sides( //set vertex lists + // ICOORDELT_LIST *left, //list of left vertices + // ICOORDELT_LIST *right); //list of right vertices + + ~BLOCK () { //destructor + } + + void set_stats( //set space size etc. + BOOL8 prop, //proportional + INT16 kern, //inter char size + INT16 space, //inter word size + INT16 ch_pitch) { //pitch if fixed + proportional = prop; + kerning = (INT8) kern; + spacing = space; + pitch = ch_pitch; + } + void set_xheight( //set char size + INT32 height) { + xheight = height; + } + void set_font_class( //set font class + INT16 font) { + font_class = font; + } + // TEXT_REGION* text_region() + // { + // return hand_block; + // } + // POLY_BLOCK* poly_block() + // { + // return hand_poly; + // } + BOOL8 prop() const { //return proportional + return proportional; + } + INT32 fixed_pitch() const { //return pitch + return pitch; + } + INT16 kern() const { //return kerning + return kerning; + } + INT16 font() const { //return font class + return font_class; + } + INT16 space() const { //return spacing + return spacing; + } + const char *name() const { //return filename + return filename.string (); + } + INT32 x_height() const { //return xheight + return xheight; + } + ROW_LIST *row_list() { //get rows + return &rows; + } + C_BLOB_LIST *blob_list() { //get blobs + return &c_blobs; + } + C_BLOB_LIST *reject_blobs() { + return &rej_blobs; + } + // void bounding_box( //get box + // ICOORD& bottom_left, //bottom left + // ICOORD& top_right) const //topright + // { + // bottom_left=box.botleft(); + // top_right=box.topright(); + // } + // const BOX& bounding_box() const //get real box + // { + // return box; + // } + + // BOOL8 contains( //is pt inside block + // ICOORD pt); + + // void move( // reposition block + // const ICOORD vec); // by vector + + void sort_rows(); //decreasing y order + + void compress(); //shrink white space + + void check_pitch(); //check proportional + + void compress( //shrink white space + const ICOORD vec); //and move by vector + + void print( //print summary/table + FILE *fp, //file to print on + BOOL8 dump); //dump whole table + + // void plot( //draw histogram + // WINDOW window, //window to draw in + // INT32 serial, //serial number + // COLOUR colour); //colour to draw in + + // void show( //show image + // IMAGE *image, //image to show + // WINDOW window); //window to show in + + void prep_serialise() { //set ptrs to counts + filename.prep_serialise (); + rows.prep_serialise (); + c_blobs.prep_serialise (); + rej_blobs.prep_serialise (); + leftside.prep_serialise (); + rightside.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + filename.dump (f); + rows.dump (f); + c_blobs.dump (f); + rej_blobs.dump (f); + leftside.dump (f); + rightside.dump (f); + if (hand_block != NULL) + hand_block->serialise (f); + } + + void de_dump( //read external bits + FILE *f) { + filename.de_dump (f); + rows.de_dump (f); + c_blobs.de_dump (f); + rej_blobs.de_dump (f); + leftside.de_dump (f); + rightside.de_dump (f); + if (hand_block != NULL) + hand_block = TEXT_REGION::de_serialise (f); + } + + //assignment + make_serialise (BLOCK) BLOCK & operator= ( + const BLOCK & source); //from this + + private: + BOOL8 proportional; //proportional + INT8 kerning; //inter blob gap + INT16 spacing; //inter word gap + INT16 pitch; //pitch of non-props + INT16 font_class; //correct font class + INT32 xheight; //height of chars + STRING filename; //name of block + // TEXT_REGION* hand_block; //if it exists + // POLY_BLOCK* hand_poly; //wierd as well + ROW_LIST rows; //rows in block + C_BLOB_LIST c_blobs; //before textord + C_BLOB_LIST rej_blobs; //duff stuff + // ICOORDELT_LIST leftside; //left side vertices + // ICOORDELT_LIST rightside; //right side vertices + // BOX box; //bounding box +}; + +int decreasing_top_order( // + const void *row1, + const void *row2); +#endif diff --git a/ccstruct/ocrrow.cpp b/ccstruct/ocrrow.cpp new file mode 100644 index 0000000000..fd40c6b8aa --- /dev/null +++ b/ccstruct/ocrrow.cpp @@ -0,0 +1,216 @@ +/********************************************************************** + * File: ocrrow.cpp (Formerly row.c) + * Description: Code for the ROW class. + * Author: Ray Smith + * Created: Tue Oct 08 15:58:04 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "ocrrow.h" +#include "blobbox.h" + +ELISTIZE_S (ROW) +/********************************************************************** + * ROW::ROW + * + * Constructor to build a ROW. Only the stats stuff are given here. + * The words are added directly. + **********************************************************************/ +ROW::ROW ( //constructor +INT32 spline_size, //no of segments +INT32 * xstarts, //segment boundaries +double *coeffs, //coefficients +float x_height, //line height +float ascenders, //ascender size +float descenders, //descender drop +INT16 kern, //char gap +INT16 space //word gap +): +baseline(spline_size, xstarts, coeffs) { + kerning = kern; //just store stuff + spacing = space; + xheight = x_height; + ascrise = ascenders; + descdrop = descenders; +} + + +/********************************************************************** + * ROW::ROW + * + * Constructor to build a ROW. Only the stats stuff are given here. + * The words are added directly. + **********************************************************************/ + +ROW::ROW( //constructor + TO_ROW *to_row, //source row + INT16 kern, //char gap + INT16 space //word gap + ) { + kerning = kern; //just store stuff + spacing = space; + xheight = to_row->xheight; + ascrise = to_row->ascrise; + descdrop = to_row->descdrop; + baseline = to_row->baseline; +} + + +/********************************************************************** + * ROW::recalc_bounding_box + * + * Set the bounding box correctly + **********************************************************************/ + +void ROW::recalc_bounding_box() { //recalculate BB + WERD *word; //current word + WERD_IT it = &words; //words of ROW + INT16 left; //of word + INT16 prev_left; //old left + + if (!it.empty ()) { + word = it.data (); + prev_left = word->bounding_box ().left (); + it.forward (); + while (!it.at_first ()) { + word = it.data (); + left = word->bounding_box ().left (); + if (left < prev_left) { + it.move_to_first (); + //words in BB order + it.sort (word_comparator); + break; + } + prev_left = left; + it.forward (); + } + } + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + word = it.data (); + if (it.at_first ()) + word->set_flag (W_BOL, TRUE); + else + //not start of line + word->set_flag (W_BOL, FALSE); + if (it.at_last ()) + word->set_flag (W_EOL, TRUE); + else + //not end of line + word->set_flag (W_EOL, FALSE); + //extend BB as reqd + bound_box += word->bounding_box (); + } +} + + +/********************************************************************** + * ROW::move + * + * Reposition row by vector + **********************************************************************/ + +void ROW::move( // reposition row + const ICOORD vec // by vector + ) { + WERD_IT it(&words); // word iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->move (vec); + + bound_box.move (vec); + baseline.move (vec); +} + + +/********************************************************************** + * ROW::print + * + * Display members + **********************************************************************/ + +void ROW::print( //print + FILE *fp //file to print on + ) { + tprintf ("Kerning= %d\n", kerning); + tprintf ("Spacing= %d\n", spacing); + bound_box.print (); + tprintf ("Xheight= %f\n", xheight); + tprintf ("Ascrise= %f\n", ascrise); + tprintf ("Descdrop= %f\n", descdrop); +} + + +/********************************************************************** + * ROW::plot + * + * Draw the ROW in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void ROW::plot( //draw it + WINDOW window, //window to draw in + COLOUR colour //colour to draw in + ) { + WERD *word; //current word + WERD_IT it = &words; //words of ROW + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + word = it.data (); + word->plot (window, colour); //all in one colour + } +} +#endif + +/********************************************************************** + * ROW::plot + * + * Draw the ROW in rainbow colours. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void ROW::plot( //draw it + WINDOW window //window to draw in + ) { + WERD *word; //current word + WERD_IT it = &words; //words of ROW + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + word = it.data (); + word->plot (window); //in rainbow colours + } +} +#endif + +/********************************************************************** + * ROW::operator= + * + * Assign rows by duplicating the row structure but NOT the WERDLIST + **********************************************************************/ + +ROW & ROW::operator= ( //assignment +const ROW & source //from this +) { + this->ELIST_LINK::operator= (source); + kerning = source.kerning; + spacing = source.spacing; + xheight = source.xheight; + ascrise = source.ascrise; + descdrop = source.descdrop; + if (!words.empty ()) + words.clear (); + baseline = source.baseline; //QSPLINES must do = + bound_box = source.bound_box; + return *this; +} diff --git a/ccstruct/ocrrow.h b/ccstruct/ocrrow.h new file mode 100644 index 0000000000..6c9389300f --- /dev/null +++ b/ccstruct/ocrrow.h @@ -0,0 +1,133 @@ +/********************************************************************** + * File: ocrrow.h (Formerly row.h) + * Description: Code for the ROW class. + * Author: Ray Smith + * Created: Tue Oct 08 15:58:04 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef OCRROW_H +#define OCRROW_H + +#include +#include "quspline.h" +#include "werd.h" + +class TO_ROW; + +class ROW:public ELIST_LINK +{ + friend void tweak_row_baseline(ROW *); + public: + ROW() { + } //empty constructor + ROW( //constructor + INT32 spline_size, //no of segments + INT32 *xstarts, //segment boundaries + double *coeffs, //coefficients //ascender size + float x_height, + float ascenders, + float descenders, //descender size + INT16 kern, //char gap + INT16 space); //word gap + ROW( //constructor + TO_ROW *row, //textord row + INT16 kern, //char gap + INT16 space); //word gap + + WERD_LIST *word_list() { //get words + return &words; + } + + float base_line( //compute baseline + float xpos) const { //at the position + //get spline value + return (float) baseline.y (xpos); + } + float x_height() const { //return x height + return xheight; + } + INT32 kern() const { //return kerning + return kerning; + } + INT32 space() const { //return spacing + return spacing; + } + float ascenders() const { //return size + return ascrise; + } + float descenders() const { //return size + return descdrop; + } + BOX bounding_box() const { //return bounding box + return bound_box; + } + + void recalc_bounding_box(); //recalculate BB + + void move( // reposition row + const ICOORD vec); // by vector + + void print( //print + FILE *fp); //file to print on + + void plot( //draw one + WINDOW window, //window to draw in + COLOUR colour); //uniform colour + void plot( //draw one + WINDOW window); //in rainbow colours + +#ifndef GRAPHICS_DISABLED + void plot_baseline( //draw the baseline + WINDOW window, //window to draw in + COLOUR colour) { //colour to draw + //draw it + baseline.plot (window, colour); + } +#endif + + void prep_serialise() { //set ptrs to counts + words.prep_serialise (); + baseline.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + words.dump (f); + baseline.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + words.de_dump (f); + baseline.de_dump (f); + } + + //assignment + make_serialise (ROW) ROW & operator= ( + const ROW & source); //from this + + private: + INT32 kerning; //inter char gap + INT32 spacing; //inter word gap + BOX bound_box; //bounding box + float xheight; //height of line + float ascrise; //size of ascenders + float descdrop; //-size of descenders + WERD_LIST words; //words + QSPLINE baseline; //baseline spline +}; + +ELISTIZEH_S (ROW) +#endif diff --git a/ccstruct/pageblk.cpp b/ccstruct/pageblk.cpp new file mode 100644 index 0000000000..c3325ea3d0 --- /dev/null +++ b/ccstruct/pageblk.cpp @@ -0,0 +1,879 @@ +#include "mfcpch.h" +#include "pageblk.h" +#include +#include +#include +#ifdef __UNIX__ +#include +#else +#include +#endif + +#include "hpddef.h" //must be last (handpd.dll) + +#define G_START 0 +#define I_START 1 +#define R_START 3 +#define S_START 5 + +extern char blabel[NUM_BLOCK_ATTR][4][MAXLENGTH]; +extern char backlabel[NUM_BACKGROUNDS][MAXLENGTH]; + +ELISTIZE_S (PAGE_BLOCK) +void PAGE_BLOCK::pb_delete() { + switch (pb_type) { + case PB_TEXT: + delete ((TEXT_BLOCK *) this); + break; + case PB_GRAPHICS: + delete ((GRAPHICS_BLOCK *) this); + break; + case PB_IMAGE: + delete ((IMAGE_BLOCK *) this); + break; + case PB_RULES: + delete ((RULE_BLOCK *) this); + break; + case PB_SCRIBBLE: + delete ((SCRIBBLE_BLOCK *) this); + break; + case PB_WEIRD: + delete ((WEIRD_BLOCK *) this); + break; + default: + break; + } +} + + +#define QUOTE_IT( parm ) #parm + +void PAGE_BLOCK::serialise(FILE *f) { + + if (fwrite (&pb_type, sizeof (PB_TYPE), 1, f) != 1) + WRITEFAILED.error (QUOTE_IT (PAGE_BLOCK::serialise), ABORT, NULL); + switch (pb_type) { + case PB_TEXT: + ((TEXT_BLOCK *) this)->serialise (f); + break; + case PB_GRAPHICS: + ((GRAPHICS_BLOCK *) this)->serialise (f); + break; + case PB_RULES: + ((RULE_BLOCK *) this)->serialise (f); + break; + case PB_IMAGE: + ((IMAGE_BLOCK *) this)->serialise (f); + break; + case PB_SCRIBBLE: + ((SCRIBBLE_BLOCK *) this)->serialise (f); + break; + case PB_WEIRD: + ((WEIRD_BLOCK *) this)->serialise (f); + break; + default: + break; + } +} + + +PAGE_BLOCK *PAGE_BLOCK::de_serialise(FILE *f) { + PB_TYPE type; + TEXT_BLOCK *tblock; + GRAPHICS_BLOCK *gblock; + RULE_BLOCK *rblock; + IMAGE_BLOCK *iblock; + SCRIBBLE_BLOCK *sblock; + WEIRD_BLOCK *wblock; + + if (fread ((void *) &type, sizeof (PB_TYPE), 1, f) != 1) + WRITEFAILED.error (QUOTE_IT (PAGE_BLOCK::serialise), ABORT, NULL); + switch (type) { + case PB_TEXT: + tblock = (TEXT_BLOCK *) alloc_struct (sizeof (TEXT_BLOCK)); + return tblock->de_serialise (f); + case PB_GRAPHICS: + gblock = (GRAPHICS_BLOCK *) alloc_struct (sizeof (GRAPHICS_BLOCK)); + return gblock->de_serialise (f); + case PB_RULES: + rblock = (RULE_BLOCK *) alloc_struct (sizeof (RULE_BLOCK)); + return rblock->de_serialise (f); + case PB_IMAGE: + iblock = (IMAGE_BLOCK *) alloc_struct (sizeof (IMAGE_BLOCK)); + return iblock->de_serialise (f); + case PB_SCRIBBLE: + sblock = (SCRIBBLE_BLOCK *) alloc_struct (sizeof (SCRIBBLE_BLOCK)); + return sblock->de_serialise (f); + case PB_WEIRD: + wblock = (WEIRD_BLOCK *) alloc_struct (sizeof (SCRIBBLE_BLOCK)); + return wblock->de_serialise (f); + default: + return NULL; + } +} + + +/********************************************************************** + * PAGE_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void PAGE_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + serialise_INT32(f, pb_type); + switch (pb_type) { + case PB_TEXT: + ((TEXT_BLOCK *) this)->serialise_asc (f); + break; + case PB_GRAPHICS: + ((GRAPHICS_BLOCK *) this)->serialise_asc (f); + break; + case PB_RULES: + ((RULE_BLOCK *) this)->serialise_asc (f); + break; + case PB_IMAGE: + ((IMAGE_BLOCK *) this)->serialise_asc (f); + break; + case PB_SCRIBBLE: + ((SCRIBBLE_BLOCK *) this)->serialise_asc (f); + break; + case PB_WEIRD: + ((WEIRD_BLOCK *) this)->serialise_asc (f); + break; + default: + break; + } +} + + +/********************************************************************** + * PAGE_BLOCK::internal_serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void PAGE_BLOCK::internal_serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((POLY_BLOCK *) this)->serialise_asc (f); + serialise_INT32(f, pb_type); + children.serialise_asc (f); +} + + +/********************************************************************** + * PAGE_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void PAGE_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + PAGE_BLOCK *page_block; //new block for list + INT32 len; /*length to retrive */ + PAGE_BLOCK_IT it; + + ((POLY_BLOCK *) this)->de_serialise_asc (f); + pb_type = (PB_TYPE) de_serialise_INT32 (f); + // children.de_serialise_asc(f); + len = de_serialise_INT32 (f); + it.set_to_list (&children); + for (; len > 0; len--) { + page_block = new_de_serialise_asc (f); + it.add_to_end (page_block); /*put on the list */ + } +} + + +/********************************************************************** + * PAGE_BLOCK::new_de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +PAGE_BLOCK *PAGE_BLOCK::new_de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + PB_TYPE type; + TEXT_BLOCK *tblock; + GRAPHICS_BLOCK *gblock; + RULE_BLOCK *rblock; + IMAGE_BLOCK *iblock; + SCRIBBLE_BLOCK *sblock; + WEIRD_BLOCK *wblock; + + type = (PB_TYPE) de_serialise_INT32 (f); + switch (type) { + case PB_TEXT: + tblock = new TEXT_BLOCK; + tblock->de_serialise_asc (f); + return tblock; + case PB_GRAPHICS: + gblock = new GRAPHICS_BLOCK; + gblock->de_serialise_asc (f); + return gblock; + case PB_RULES: + rblock = new RULE_BLOCK; + rblock->de_serialise_asc (f); + return rblock; + case PB_IMAGE: + iblock = new IMAGE_BLOCK; + iblock->de_serialise_asc (f); + return iblock; + case PB_SCRIBBLE: + sblock = new SCRIBBLE_BLOCK; + sblock->de_serialise_asc (f); + return sblock; + case PB_WEIRD: + wblock = new WEIRD_BLOCK; + wblock->de_serialise_asc (f); + return wblock; + default: + return NULL; + } +} + + +void PAGE_BLOCK::show_attrs(DEBUG_WIN *f) { + PAGE_BLOCK_IT it; + + switch (pb_type) { + case PB_TEXT: + ((TEXT_BLOCK *) this)->show_attrs (f); + break; + case PB_GRAPHICS: + ((GRAPHICS_BLOCK *) this)->show_attrs (f); + break; + case PB_RULES: + ((RULE_BLOCK *) this)->show_attrs (f); + break; + case PB_IMAGE: + ((IMAGE_BLOCK *) this)->show_attrs (f); + break; + case PB_SCRIBBLE: + ((SCRIBBLE_BLOCK *) this)->show_attrs (f); + break; + case PB_WEIRD: + ((WEIRD_BLOCK *) this)->show_attrs (f); + break; + default: + break; + } + + if (!children.empty ()) { + f->dprintf ("containing subblocks\n"); + it.set_to_list (&children); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->show_attrs (f); + f->dprintf ("end of subblocks\n"); + } +} + + +PAGE_BLOCK::PAGE_BLOCK (ICOORDELT_LIST * points, PB_TYPE type, PAGE_BLOCK_LIST * child):POLY_BLOCK (points, +POLY_PAGE) { + PAGE_BLOCK_IT + c = &children; + + pb_type = type; + children.clear (); + c.move_to_first (); + c.add_list_before (child); +} + + +PAGE_BLOCK::PAGE_BLOCK (ICOORDELT_LIST * points, PB_TYPE type):POLY_BLOCK (points, +POLY_PAGE) { + pb_type = type; + children.clear (); +} + + +void PAGE_BLOCK::add_a_child(PAGE_BLOCK *newchild) { + PAGE_BLOCK_IT c = &children; + + c.move_to_first (); + c.add_to_end (newchild); +} + + +/********************************************************************** + * PAGE_BLOCK::rotate + * + * Rotate the PAGE_BLOCK and its children + **********************************************************************/ + +void PAGE_BLOCK::rotate( //cos,sin + FCOORD rotation) { + //sub block iterator + PAGE_BLOCK_IT child_it = &children; + PAGE_BLOCK *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->rotate (rotation); + } + if (pb_type == PB_TEXT) + ((TEXT_BLOCK *) this)->rotate (rotation); + else + POLY_BLOCK::rotate(rotation); +} + + +/********************************************************************** + * PAGE_BLOCK::move + * + * Move the PAGE_BLOCK and its children + **********************************************************************/ + +void PAGE_BLOCK::move(ICOORD shift //amount to move + ) { + //sub block iterator + PAGE_BLOCK_IT child_it = &children; + PAGE_BLOCK *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->move (shift); + } + if (pb_type == PB_TEXT) + ((TEXT_BLOCK *) this)->move (shift); + else + POLY_BLOCK::move(shift); +} + +#ifndef GRAPHICS_DISABLED +void PAGE_BLOCK::basic_plot(WINDOW window, COLOUR colour) { + PAGE_BLOCK_IT c = &children; + + POLY_BLOCK::plot (window, colour, 0); + + if (!c.empty ()) + for (c.mark_cycle_pt (); !c.cycled_list (); c.forward ()) + c.data ()->plot (window, colour); +} + + +void PAGE_BLOCK::plot(WINDOW window, COLOUR colour) { + TEXT_BLOCK *tblock; + WEIRD_BLOCK *wblock; + + switch (pb_type) { + case PB_TEXT: + basic_plot(window, colour); + tblock = (TEXT_BLOCK *) this; + tblock->plot (window, colour, REGION_COLOUR, SUBREGION_COLOUR); + break; + case PB_WEIRD: + wblock = (WEIRD_BLOCK *) this; + wblock->plot (window, colour); + break; + default: + basic_plot(window, colour); + break; + } +} +#endif + +void show_all_in(PAGE_BLOCK *pblock, POLY_BLOCK *show_area, DEBUG_WIN *f) { + PAGE_BLOCK_IT c; + INT16 i, pnum; + + c.set_to_list (pblock->child ()); + pnum = pblock->child ()->length (); + for (i = 0; i < pnum; i++, c.forward ()) { + if (show_area->contains (c.data ())) + c.data ()->show_attrs (f); + else if (show_area->overlap (c.data ())) + show_all_in (c.data (), show_area, f); + } +} + + +void delete_all_in(PAGE_BLOCK *pblock, POLY_BLOCK *delete_area) { + PAGE_BLOCK_IT c; + INT16 i, pnum; + + c.set_to_list (pblock->child ()); + pnum = pblock->child ()->length (); + for (i = 0; i < pnum; i++, c.forward ()) { + if (delete_area->contains (c.data ())) + c.extract ()->pb_delete (); + else if (delete_area->overlap (c.data ())) + delete_all_in (c.data (), delete_area); + } +} + + +PAGE_BLOCK *smallest_containing(PAGE_BLOCK *pblock, POLY_BLOCK *other) { + PAGE_BLOCK_IT c; + + c.set_to_list (pblock->child ()); + if (c.empty ()) + return (pblock); + + for (c.mark_cycle_pt (); !c.cycled_list (); c.forward ()) + if (c.data ()->contains (other)) + return (smallest_containing (c.data (), other)); + + return (pblock); +} + + +TEXT_BLOCK::TEXT_BLOCK (ICOORDELT_LIST * points, BOOL8 backg[NUM_BACKGROUNDS]):PAGE_BLOCK (points, +PB_TEXT) { + int + i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); + + text_regions.clear (); +} + + +void +TEXT_BLOCK::set_attrs (BOOL8 backg[NUM_BACKGROUNDS]) { + int i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); +} + + +void TEXT_BLOCK::add_a_region(TEXT_REGION *newchild) { + TEXT_REGION_IT c; + + c.set_to_list (&text_regions); + + c.move_to_first (); + c.add_to_end (newchild); +} + + +/********************************************************************** + * TEXT_BLOCK::rotate + * + * Rotate the TEXT_BLOCK and its children + **********************************************************************/ + +void TEXT_BLOCK::rotate( //cos,sin + FCOORD rotation) { + //sub block iterator + TEXT_REGION_IT child_it = &text_regions; + TEXT_REGION *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->rotate (rotation); + } + POLY_BLOCK::rotate(rotation); +} + + +/********************************************************************** + * TEXT_BLOCK::move + * + * Move the TEXT_BLOCK and its children + **********************************************************************/ + +void TEXT_BLOCK::move(ICOORD shift //amount to move + ) { + //sub block iterator + TEXT_REGION_IT child_it = &text_regions; + TEXT_REGION *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->move (shift); + } + POLY_BLOCK::move(shift); +} + + +/********************************************************************** + * TEXT_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void TEXT_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32 (f, background.val); + text_regions.serialise_asc (f); +} + + +/********************************************************************** + * TEXT_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void TEXT_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + background.val = de_serialise_INT32 (f); + text_regions.de_serialise_asc (f); +} + + +#ifndef GRAPHICS_DISABLED +void TEXT_BLOCK::plot(WINDOW window, + COLOUR colour, + COLOUR region_colour, + COLOUR subregion_colour) { + TEXT_REGION_IT t = &text_regions, tc; + + PAGE_BLOCK::basic_plot(window, colour); + + if (!t.empty ()) + for (t.mark_cycle_pt (); !t.cycled_list (); t.forward ()) { + t.data ()->plot (window, region_colour, t.data ()->id_no ()); + tc.set_to_list (t.data ()->regions ()); + if (!tc.empty ()) + for (tc.mark_cycle_pt (); !tc.cycled_list (); tc.forward ()) + tc.data ()->plot (window, subregion_colour, -1); + } +} +#endif + + +void TEXT_BLOCK::show_attrs(DEBUG_WIN *f) { + TEXT_REGION_IT it; + + f->dprintf ("TEXT BLOCK\n"); + print_background(f, background); + if (!text_regions.empty ()) { + f->dprintf ("containing text regions:\n"); + it.set_to_list (&text_regions); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->show_attrs (f); + f->dprintf ("end of regions\n"); + } +} + + +DLLSYM void show_all_tr_in(TEXT_BLOCK *tblock, + POLY_BLOCK *show_area, + DEBUG_WIN *f) { + TEXT_REGION_IT t, tc; + INT16 i, tnum, j, ttnum; + + t.set_to_list (tblock->regions ()); + tnum = tblock->regions ()->length (); + for (i = 0; i < tnum; i++, t.forward ()) { + if (show_area->contains (t.data ())) + t.data ()->show_attrs (f); + else if (show_area->overlap (t.data ())) { + tc.set_to_list (t.data ()->regions ()); + ttnum = t.data ()->regions ()->length (); + for (j = 0; j < ttnum; j++, tc.forward ()) + if (show_area->contains (tc.data ())) + tc.data ()->show_attrs (f); + } + } +} + + +void delete_all_tr_in(TEXT_BLOCK *tblock, POLY_BLOCK *delete_area) { + TEXT_REGION_IT t, tc; + INT16 i, tnum, j, ttnum; + + t.set_to_list (tblock->regions ()); + tnum = tblock->regions ()->length (); + for (i = 0; i < tnum; i++, t.forward ()) { + if (delete_area->contains (t.data ())) + delete (t.extract ()); + else if (delete_area->overlap (t.data ())) { + tc.set_to_list (t.data ()->regions ()); + ttnum = t.data ()->regions ()->length (); + for (j = 0; j < ttnum; j++, tc.forward ()) + if (delete_area->contains (tc.data ())) + delete (tc.extract ()); + } + } +} + + +RULE_BLOCK::RULE_BLOCK (ICOORDELT_LIST * points, INT8 sing, INT8 colo):PAGE_BLOCK (points, +PB_RULES) { + multiplicity = sing; + colour = colo; +} + + +void RULE_BLOCK::set_attrs(INT8 sing, INT8 colo) { + multiplicity = sing; + colour = colo; +} + + +void RULE_BLOCK::show_attrs(DEBUG_WIN *f) { + f->dprintf ("RULE BLOCK with attributes %s, %s\n", + blabel[R_START][multiplicity], blabel[R_START + 1][colour]); +} + + +/********************************************************************** + * RULE_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void RULE_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32(f, multiplicity); + serialise_INT32(f, colour); +} + + +/********************************************************************** + * RULE_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void RULE_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + multiplicity = de_serialise_INT32 (f); + colour = de_serialise_INT32 (f); +} + + +GRAPHICS_BLOCK::GRAPHICS_BLOCK (ICOORDELT_LIST * points, BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg):PAGE_BLOCK (points, +PB_GRAPHICS) { + int + i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); + + foreground = foreg; +} + + +void +GRAPHICS_BLOCK::set_attrs (BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg) { + int i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); + + foreground = foreg; +} + + +void GRAPHICS_BLOCK::show_attrs(DEBUG_WIN *f) { + f->dprintf ("GRAPHICS BLOCK with attribute %s\n", + blabel[G_START][foreground]); + print_background(f, background); +} + + +/********************************************************************** + * GRAPHICS_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void GRAPHICS_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32 (f, background.val); + serialise_INT32(f, foreground); +} + + +/********************************************************************** + * GRAPHICS_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void GRAPHICS_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + background.val = de_serialise_INT32 (f); + foreground = de_serialise_INT32 (f); +} + + +IMAGE_BLOCK::IMAGE_BLOCK (ICOORDELT_LIST * points, INT8 colo, INT8 qual):PAGE_BLOCK (points, +PB_IMAGE) { + colour = colo; + quality = qual; +} + + +void IMAGE_BLOCK::set_attrs(INT8 colo, INT8 qual) { + colour = colo; + quality = qual; +} + + +void IMAGE_BLOCK::show_attrs(DEBUG_WIN *f) { + f->dprintf ("IMAGE BLOCK with attributes %s, %s\n", blabel[I_START][colour], + blabel[I_START + 1][quality]); +} + + +/********************************************************************** + * IMAGE_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void IMAGE_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32(f, colour); + serialise_INT32(f, quality); +} + + +/********************************************************************** + * IMAGE_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void IMAGE_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + colour = de_serialise_INT32 (f); + quality = de_serialise_INT32 (f); +} + + +SCRIBBLE_BLOCK::SCRIBBLE_BLOCK (ICOORDELT_LIST * points, BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg):PAGE_BLOCK (points, +PB_SCRIBBLE) { + int + i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); + + foreground = foreg; +} + + +void +SCRIBBLE_BLOCK::set_attrs (BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg) { + int i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); + + foreground = foreg; +} + + +void SCRIBBLE_BLOCK::show_attrs(DEBUG_WIN *f) { + f->dprintf ("SCRIBBLE BLOCK with attributes %s\n", + blabel[S_START][foreground]); + print_background(f, background); +} + + +/********************************************************************** + * SCRIBBLE_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void SCRIBBLE_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32 (f, background.val); + serialise_INT32(f, foreground); +} + + +/********************************************************************** + * SCRIBBLE_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void SCRIBBLE_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + background.val = de_serialise_INT32 (f); + foreground = de_serialise_INT32 (f); +} + + +WEIRD_BLOCK::WEIRD_BLOCK (ICOORDELT_LIST * points, INT32 id_no):PAGE_BLOCK (points, +PB_WEIRD) { + id_number = id_no; +} + + +#ifndef GRAPHICS_DISABLED +void WEIRD_BLOCK::plot(WINDOW window, COLOUR colour) { + PAGE_BLOCK_IT c = this->child (); + + POLY_BLOCK::plot(window, colour, id_number); + + if (!c.empty ()) + for (c.mark_cycle_pt (); !c.cycled_list (); c.forward ()) + c.data ()->plot (window, colour); +} +#endif + + +void WEIRD_BLOCK::set_id(INT32 id_no) { + id_number = id_no; +} + + +void WEIRD_BLOCK::show_attrs(DEBUG_WIN *f) { + f->dprintf ("WEIRD BLOCK with id number %d\n", id_number); +} + + +/********************************************************************** + * WEIRD_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void WEIRD_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32(f, id_number); +} + + +/********************************************************************** + * WEIRD_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void WEIRD_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + id_number = de_serialise_INT32 (f); +} + + +void print_background(DEBUG_WIN *f, BITS16 background) { + int i; + + f->dprintf ("Background is \n"); + for (i = 0; i < NUM_BACKGROUNDS; i++) { + if (background.bit (i)) + f->dprintf ("%s, ", backlabel[i]); + } + + f->dprintf ("\n"); + +} diff --git a/ccstruct/pageblk.h b/ccstruct/pageblk.h new file mode 100644 index 0000000000..bc021ef33a --- /dev/null +++ b/ccstruct/pageblk.h @@ -0,0 +1,318 @@ +#ifndef PAGEBLK_C +#define PAGEBLK_C + +#include "elst.h" +#include "txtregn.h" +#include "bits16.h" + +#include "hpddef.h" //must be last (handpd.dll) + +enum PB_TYPE +{ + PB_TEXT, + PB_RULES, + PB_GRAPHICS, + PB_IMAGE, + PB_SCRIBBLE, + PB_WEIRD +}; + +class DLLSYM PAGE_BLOCK; //forward decl +class DLLSYM TEXT_BLOCK; //forward decl +class DLLSYM GRAPHICS_BLOCK; //forward decl +class DLLSYM RULE_BLOCK; //forward decl +class DLLSYM IMAGE_BLOCK; //forward decl +class DLLSYM SCRIBBLE_BLOCK; //forward decl +class DLLSYM WEIRD_BLOCK; //forward decl + +ELISTIZEH_S (PAGE_BLOCK) +class DLLSYM PAGE_BLOCK:public ELIST_LINK, public POLY_BLOCK +//page block +{ + public: + PAGE_BLOCK() { + } //empty constructor + PAGE_BLOCK( //simple constructor + ICOORDELT_LIST *points, + PB_TYPE type, + PAGE_BLOCK_LIST *child); + + PAGE_BLOCK( //simple constructor + ICOORDELT_LIST *points, + PB_TYPE type); + + ~PAGE_BLOCK () { //destructor + } + + void add_a_child(PAGE_BLOCK *newchild); + + PB_TYPE type() { //get type + return pb_type; + } + + PAGE_BLOCK_LIST *child() { //get children + return &children; + } + + void rotate( //rotate it + FCOORD rotation); + void move( //move it + ICOORD shift); //vector + + void basic_plot(WINDOW window, COLOUR colour); + + void plot(WINDOW window, COLOUR colour); + + void show_attrs(DEBUG_WIN *debug); + + NEWDELETE2 (PAGE_BLOCK) void pb_delete (); + + void serialise(FILE *f); + + static PAGE_BLOCK *de_serialise(FILE *f); + + void prep_serialise() { //set ptrs to counts + POLY_BLOCK::prep_serialise(); + children.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + POLY_BLOCK::dump(f); + children.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + POLY_BLOCK::de_dump(f); + children.de_dump (f); + } + + //note that due to the awful switched nature of the PAGE_BLOCK class, + //a PAGE_BLOCK_LIST cannot be de-serialised by the normal mechanism, since + //each element cannot be de-serialised in place. + //To fix this it is important to use read_poly_blocks or the code therein. + void serialise_asc( //serialise to ascii + FILE *f); + void internal_serialise_asc( //serialise to ascii + FILE *f); + void de_serialise_asc( //serialise from ascii + FILE *f); + //make one from ascii + static PAGE_BLOCK *new_de_serialise_asc(FILE *f); + + private: + PB_TYPE pb_type; + PAGE_BLOCK_LIST children; +}; + +DLLSYM void show_all_in(PAGE_BLOCK *pblock, + POLY_BLOCK *show_area, + DEBUG_WIN *f); + +DLLSYM void delete_all_in(PAGE_BLOCK *pblock, POLY_BLOCK *delete_area); + +DLLSYM PAGE_BLOCK *smallest_containing(PAGE_BLOCK *pblock, POLY_BLOCK *other); + +class DLLSYM TEXT_BLOCK:public PAGE_BLOCK + //text block +{ + public: + TEXT_BLOCK() { + } //empty constructor + TEXT_BLOCK(ICOORDELT_LIST *points); + + TEXT_BLOCK (ICOORDELT_LIST * points, BOOL8 backg[NUM_BACKGROUNDS]); + + //get children + TEXT_REGION_LIST *regions() { + return &text_regions; + } + + INT32 nregions() { + return text_regions.length (); + } + + void add_a_region(TEXT_REGION *newchild); + + void rotate( //rotate it + FCOORD rotation); + void move( //move it + ICOORD shift); //vector + + void plot(WINDOW window, + COLOUR colour, + COLOUR region_colour, + COLOUR subregion_colour); + + void set_attrs (BOOL8 backg[NUM_BACKGROUNDS]); + + void show_attrs(DEBUG_WIN *debug); + + void prep_serialise() { //set ptrs to counts + PAGE_BLOCK::prep_serialise(); + text_regions.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + PAGE_BLOCK::dump(f); + text_regions.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + PAGE_BLOCK::de_dump(f); + text_regions.de_dump (f); + } + + //serialise to ascii + make_serialise (TEXT_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + BITS16 background; + + TEXT_REGION_LIST text_regions; +}; + +DLLSYM void delete_all_tr_in(TEXT_BLOCK *tblock, POLY_BLOCK *delete_area); + +DLLSYM void show_all_tr_in(TEXT_BLOCK *tblock, + POLY_BLOCK *show_area, + DEBUG_WIN *f); + +class DLLSYM RULE_BLOCK:public PAGE_BLOCK + //rule block +{ + public: + RULE_BLOCK() { + } //empty constructor + RULE_BLOCK(ICOORDELT_LIST *points, INT8 sing, INT8 colo); + + void set_attrs(INT8 sing, INT8 colo); + + void show_attrs(DEBUG_WIN *debug); + + //serialise to ascii + make_serialise (RULE_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + INT8 multiplicity; + INT8 colour; + +}; + +class DLLSYM GRAPHICS_BLOCK:public PAGE_BLOCK + //graphics block +{ + public: + GRAPHICS_BLOCK() { + } //empty constructor + GRAPHICS_BLOCK (ICOORDELT_LIST * points, + BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg); + + void set_attrs (BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg); + + void show_attrs(DEBUG_WIN *debug); + + //serialise to ascii + make_serialise (GRAPHICS_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + BITS16 background; + INT8 foreground; + +}; + +class DLLSYM IMAGE_BLOCK:public PAGE_BLOCK + //image block +{ + public: + IMAGE_BLOCK() { + } //empty constructor + IMAGE_BLOCK(ICOORDELT_LIST *points, INT8 colo, INT8 qual); + + void set_attrs(INT8 colo, INT8 qual); + + void show_attrs(DEBUG_WIN *debug); + + //serialise to ascii + make_serialise (IMAGE_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + INT8 colour; + INT8 quality; + +}; + +class DLLSYM SCRIBBLE_BLOCK:public PAGE_BLOCK + //scribble block +{ + public: + SCRIBBLE_BLOCK() { + } //empty constructor + SCRIBBLE_BLOCK (ICOORDELT_LIST * points, + BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg); + + void set_attrs (BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg); + + void show_attrs(DEBUG_WIN *debug); + + //serialise to ascii + make_serialise (SCRIBBLE_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + BITS16 background; + INT8 foreground; +}; + +class DLLSYM WEIRD_BLOCK:public PAGE_BLOCK + //weird block +{ + public: + WEIRD_BLOCK() { + } //empty constructor + WEIRD_BLOCK(ICOORDELT_LIST *points, INT32 id_no); + + void set_id(INT32 id_no); + + void show_attrs(DEBUG_WIN *debug); + + void set_id_no(INT32 new_id) { + id_number = new_id; + } + + void plot(WINDOW window, COLOUR colour); + + INT32 id_no() { + return id_number; + } + + //serialise to ascii + make_serialise (WEIRD_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + INT32 id_number; //unique id + +}; + +void print_background(DEBUG_WIN *f, BITS16 background); +#endif diff --git a/ccstruct/pageres.cpp b/ccstruct/pageres.cpp new file mode 100644 index 0000000000..4a542b39bd --- /dev/null +++ b/ccstruct/pageres.cpp @@ -0,0 +1,325 @@ +/********************************************************************** + * File: pageres.cpp (Formerly page_res.c) + * Description: Results classes used by control.c + * Author: Phil Cheatle + * Created: Tue Sep 22 08:42:49 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#include "mfcpch.h" +#include +#ifdef __UNIX__ +#include +#endif +#include "pageres.h" +#include "notdll.h" + +ELISTIZE (BLOCK_RES) +CLISTIZE (BLOCK_RES) ELISTIZE (ROW_RES) ELISTIZE (WERD_RES) +/************************************************************************* + * PAGE_RES::PAGE_RES + * + * Constructor for page results + *************************************************************************/ +PAGE_RES::PAGE_RES( //recursive construct + BLOCK_LIST *the_block_list //real page + ) { + BLOCK_IT block_it(the_block_list); + BLOCK_RES_IT block_res_it(&block_res_list); + + char_count = 0; + rej_count = 0; + rejected = FALSE; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block_res_it.add_to_end (new BLOCK_RES (block_it.data ())); + } +} + + +/************************************************************************* + * BLOCK_RES::BLOCK_RES + * + * Constructor for BLOCK results + *************************************************************************/ + +BLOCK_RES::BLOCK_RES( //recursive construct + BLOCK *the_block //real BLOCK + ) { + ROW_IT row_it (the_block->row_list ()); + ROW_RES_IT row_res_it(&row_res_list); + + char_count = 0; + rej_count = 0; + font_class = -1; //not assigned + x_height = -1.0; + font_assigned = FALSE; + bold = FALSE; + italic = FALSE; + row_count = 0; + + block = the_block; + + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row_res_it.add_to_end (new ROW_RES (row_it.data ())); + } +} + + +/************************************************************************* + * ROW_RES::ROW_RES + * + * Constructor for ROW results + *************************************************************************/ + +ROW_RES::ROW_RES( //recursive construct + ROW *the_row //real ROW + ) { + WERD_IT word_it (the_row->word_list ()); + WERD_RES_IT word_res_it(&word_res_list); + WERD_RES *combo = NULL; //current combination of fuzzies + WERD_RES *word_res; //current word + WERD *copy_word; + + char_count = 0; + rej_count = 0; + whole_word_rej_count = 0; + font_class = -1; + font_class_score = -1.0; + bold = FALSE; + italic = FALSE; + + row = the_row; + + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word_res = new WERD_RES (word_it.data ()); + + if (word_res->word->flag (W_FUZZY_NON)) { + ASSERT_HOST (combo != NULL); + word_res->part_of_combo = TRUE; + combo->copy_on (word_res); + } + if (word_it.data_relative (1)->flag (W_FUZZY_NON)) { + if (combo == NULL) { + copy_word = new WERD; + //deep copy + *copy_word = *(word_it.data ()); + combo = new WERD_RES (copy_word); + combo->combination = TRUE; + word_res_it.add_to_end (combo); + } + word_res->part_of_combo = TRUE; + } + else + combo = NULL; + word_res_it.add_to_end (word_res); + } +} + + +WERD_RES & WERD_RES::operator= ( //assign word_res +const WERD_RES & source //from this +) { + this->ELIST_LINK::operator= (source); + if (source.combination) { + word = new WERD; + *word = *(source.word); //deep copy + } + else + word = source.word; //pt to same word + + if (source.outword != NULL) { + outword = new WERD; + *outword = *(source.outword);//deep copy + } + else + outword = NULL; + + denorm = source.denorm; + if (source.best_choice != NULL) { + best_choice = new WERD_CHOICE; + *best_choice = *(source.best_choice); + raw_choice = new WERD_CHOICE; + *raw_choice = *(source.raw_choice); + } + else { + best_choice = NULL; + raw_choice = NULL; + } + if (source.ep_choice != NULL) { + ep_choice = new WERD_CHOICE; + *ep_choice = *(source.ep_choice); + } + else + ep_choice = NULL; + reject_map = source.reject_map; + tess_failed = source.tess_failed; + tess_accepted = source.tess_accepted; + tess_would_adapt = source.tess_would_adapt; + done = source.done; + unlv_crunch_mode = source.unlv_crunch_mode; + italic = source.italic; + bold = source.bold; + font1 = source.font1; + font1_count = source.font1_count; + font2 = source.font2; + font2_count = source.font2_count; + x_height = source.x_height; + caps_height = source.caps_height; + guessed_x_ht = source.guessed_x_ht; + guessed_caps_ht = source.guessed_caps_ht; + combination = source.combination; + part_of_combo = source.part_of_combo; + reject_spaces = source.reject_spaces; + return *this; +} + + +WERD_RES::~WERD_RES () { + if (combination) + delete word; + if (outword != NULL) + delete outword; + if (best_choice != NULL) { + delete best_choice; + delete raw_choice; + } + if (ep_choice != NULL) { + delete ep_choice; + } +} + + +/************************************************************************* + * PAGE_RES_IT::restart_page + * + * Set things up at the start of the page + *************************************************************************/ + +WERD_RES *PAGE_RES_IT::restart_page() { + block_res_it.set_to_list (&page_res->block_res_list); + block_res_it.mark_cycle_pt (); + block_res = NULL; + row_res = NULL; + word_res = NULL; + next_block_res = NULL; + next_row_res = NULL; + next_word_res = NULL; + internal_forward(TRUE); + return internal_forward (FALSE); +} + + +/************************************************************************* + * PAGE_RES_IT::internal_forward + * + * Find the next word on the page. Empty blocks and rows are skipped. + * The iterator maintains pointers to block, row and word for the previous, + * current and next words. These are correct, regardless of block/row + * boundaries. NULL values denote start and end of the page. + *************************************************************************/ + +WERD_RES *PAGE_RES_IT::internal_forward(BOOL8 new_block) { + BOOL8 found_next_word = FALSE; + BOOL8 new_row = FALSE; + + prev_block_res = block_res; + prev_row_res = row_res; + prev_word_res = word_res; + block_res = next_block_res; + row_res = next_row_res; + word_res = next_word_res; + + while (!found_next_word && !block_res_it.cycled_list ()) { + if (new_block) { + new_block = FALSE; + row_res_it.set_to_list (&block_res_it.data ()->row_res_list); + row_res_it.mark_cycle_pt (); + new_row = TRUE; + } + while (!found_next_word && !row_res_it.cycled_list ()) { + if (new_row) { + new_row = FALSE; + word_res_it.set_to_list (&row_res_it.data ()->word_res_list); + word_res_it.mark_cycle_pt (); + } + while (!found_next_word && !word_res_it.cycled_list ()) { + next_block_res = block_res_it.data (); + next_row_res = row_res_it.data (); + next_word_res = word_res_it.data (); + found_next_word = TRUE; + do { + word_res_it.forward (); + } + while (word_res_it.data ()->part_of_combo); + } + if (!found_next_word) { //end of row reached + row_res_it.forward (); + new_row = TRUE; + } + } + if (!found_next_word) { //end of block reached + block_res_it.forward (); + new_block = TRUE; + } + } + if (!found_next_word) { //end of page reached + next_block_res = NULL; + next_row_res = NULL; + next_word_res = NULL; + } + return word_res; +} + + +/************************************************************************* + * PAGE_RES_IT::forward_block + * + * Move to the first word of the next block + * Can be followed by subsequent calls to forward() BUT at the first word in + * the block, the prev block, row and word are all NULL. + *************************************************************************/ + +WERD_RES *PAGE_RES_IT::forward_block() { + if (block_res == next_block_res) { + block_res_it.forward ();; + block_res = NULL; + row_res = NULL; + word_res = NULL; + next_block_res = NULL; + next_row_res = NULL; + next_word_res = NULL; + internal_forward(TRUE); + } + return internal_forward (FALSE); +} + + +void PAGE_RES_IT::rej_stat_word() { + INT16 chars_in_word; + INT16 rejects_in_word = 0; + + chars_in_word = word_res->reject_map.length (); + page_res->char_count += chars_in_word; + block_res->char_count += chars_in_word; + row_res->char_count += chars_in_word; + + rejects_in_word = word_res->reject_map.reject_count (); + + page_res->rej_count += rejects_in_word; + block_res->rej_count += rejects_in_word; + row_res->rej_count += rejects_in_word; + if (chars_in_word == rejects_in_word) + row_res->whole_word_rej_count += rejects_in_word; +} diff --git a/ccstruct/pageres.h b/ccstruct/pageres.h new file mode 100644 index 0000000000..6ddadfba50 --- /dev/null +++ b/ccstruct/pageres.h @@ -0,0 +1,311 @@ +/********************************************************************** + * File: pageres.h (Formerly page_res.h) + * Description: Results classes used by control.c + * Author: Phil Cheatle + * Created: Tue Sep 22 08:42:49 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#ifndef PAGERES_H +#define PAGERES_H + +#include "elst.h" +#include "ocrblock.h" +#include "ocrrow.h" +#include "werd.h" +#include "ratngs.h" +#include "rejctmap.h" +#include "notdll.h" +#include "notdll.h" + +/* Forward declarations */ + +class BLOCK_RES; + +ELISTIZEH (BLOCK_RES) CLISTIZEH (BLOCK_RES) +class +ROW_RES; + +ELISTIZEH (ROW_RES) +class WERD_RES; + +ELISTIZEH (WERD_RES) +/************************************************************************* + * PAGE_RES - Page results + *************************************************************************/ +class PAGE_RES //page result +{ + public: + INT32 char_count; + INT32 rej_count; + BLOCK_RES_LIST block_res_list; + BOOL8 rejected; + + PAGE_RES() { + } //empty constructor + + PAGE_RES( //simple constructor + BLOCK_LIST *block_list); //real blocks + + ~PAGE_RES () { //destructor + } +}; + +/************************************************************************* + * BLOCK_RES - Block results + *************************************************************************/ + +class BLOCK_RES:public ELIST_LINK + //page block result +{ + public: + BLOCK * block; //real block + INT32 char_count; //chars in block + INT32 rej_count; //rejected chars + INT16 font_class; // + INT16 row_count; + float x_height; + BOOL8 font_assigned; // block already + // processed + BOOL8 bold; // all bold + BOOL8 italic; // all italic + + ROW_RES_LIST row_res_list; + + BLOCK_RES() { + } //empty constructor + + BLOCK_RES( //simple constructor + BLOCK *the_block); //real block + + ~BLOCK_RES () { //destructor + } +}; + +/************************************************************************* + * ROW_RES - Row results + *************************************************************************/ + +class ROW_RES:public ELIST_LINK //row result +{ + public: + ROW * row; //real row + INT32 char_count; //chars in block + INT32 rej_count; //rejected chars + INT32 whole_word_rej_count; //rejs in total rej wds + WERD_RES_LIST word_res_list; + float font_class_score; + INT16 font_class; // + INT32 italic; + INT32 bold; + INT8 font1; //primary font + INT8 font1_count; //no of voters + INT8 font2; //secondary font + INT8 font2_count; //no of voters + + ROW_RES() { + } //empty constructor + + ROW_RES( //simple constructor + ROW *the_row); //real row + + ~ROW_RES () { //destructor + } +}; + +/************************************************************************* + * WERD_RES - Word results + *************************************************************************/ +enum CRUNCH_MODE +{ + CR_NONE, + CR_KEEP_SPACE, + CR_LOOSE_SPACE, + CR_DELETE +}; + +class WERD_RES:public ELIST_LINK //word result +{ + public: + WERD * word; //non-bln real word + WERD *outword; //bln best choice + //segmentation + DENORM denorm; //for use on outword + WERD_CHOICE *best_choice; //tess output + WERD_CHOICE *raw_choice; //top choice permuter + WERD_CHOICE *ep_choice; //ep text + REJMAP reject_map; //best_choice rejects + BOOL8 tess_failed; + /* + If tess_failed is TRUE, one of the following tests failed when Tess + returned: + - The outword blob list was not the same length as the best_choice string; + - The best_choice string contained ALL blanks; + - The best_choice string was zero length + */ + BOOL8 tess_accepted; //Tess thinks its ok? + BOOL8 tess_would_adapt; //Tess would adapt? + BOOL8 done; //ready for output? + INT8 italic; + INT8 bold; + INT8 font1; //primary font + INT8 font1_count; //no of voters + INT8 font2; //secondary font + INT8 font2_count; //no of voters + CRUNCH_MODE unlv_crunch_mode; + float x_height; //Post match estimate + float caps_height; //Post match estimate + BOOL8 guessed_x_ht; + BOOL8 guessed_caps_ht; + /* + To deal with fuzzy spaces we need to be able to combine "words" to form + combinations when we suspect that the gap is a non-space. The (new) text + ord code generates separate words for EVERY fuzzy gap - flags in the word + indicate whether the gap is below the threshold (fuzzy kern) and is thus + NOT a real word break by default, or above the threshold (fuzzy space) and + this is a real word break by default. + + The WERD_RES list contains all these words PLUS "combination" words built + out of (copies of) the words split by fuzzy kerns. The separate parts have + their "part_of_combo" flag set true and should be IGNORED on a default + reading of the list. + + Combination words are FOLLOWED by the sequence of part_of_combo words + which they combine. + */ + BOOL8 combination; //of two fuzzy gap wds + BOOL8 part_of_combo; //part of a combo + BOOL8 reject_spaces; //Reject spacing? + + WERD_RES() { + } //empty constructor + + WERD_RES( //simple constructor + WERD *the_word) { //real word + word = the_word; + outword = NULL; + best_choice = NULL; + raw_choice = NULL; + ep_choice = NULL; + tess_failed = FALSE; + tess_accepted = FALSE; + tess_would_adapt = FALSE; + done = FALSE; + unlv_crunch_mode = CR_NONE; + italic = FALSE; + bold = FALSE; + font1 = -1; + font1_count = 0; + font2 = -1; + font2_count = 0; + x_height = 0.0; + caps_height = 0.0; + guessed_x_ht = TRUE; + guessed_caps_ht = TRUE; + combination = FALSE; + part_of_combo = FALSE; + reject_spaces = FALSE; + } + WERD_RES( //constr from WERD_RES + const WERD_RES &source) { + *this = source; //see operator= + } + + ~WERD_RES (); //destructor + + WERD_RES & operator= ( //assign word res + const WERD_RES & source); //from this + + void copy_on( //copy blobs onto word + WERD_RES *word_res) { //from this word + word->set_flag (W_EOL, word_res->word->flag (W_EOL)); + word->copy_on (word_res->word); + } +}; + +/************************************************************************* + * PAGE_RES_IT - Page results iterator + *************************************************************************/ + +class PAGE_RES_IT +{ + public: + PAGE_RES * page_res; //page being iterated + + PAGE_RES_IT() { + } //empty contructor + + PAGE_RES_IT( //empty contructor + PAGE_RES *the_page_res) { //page result + page_res = the_page_res; + restart_page(); //ready to scan + } + + WERD_RES *restart_page(); //get ready + + WERD_RES *internal_forward( //get next word + BOOL8 new_block); + + WERD_RES *forward() { //get next word + return internal_forward (FALSE); + } + + WERD_RES *forward_block(); //get first word in + //next non-empty block + WERD_RES *prev_word() { //previous word + return prev_word_res; + } + ROW_RES *prev_row() { //row of prev word + return prev_row_res; + } + BLOCK_RES *prev_block() { //block of prev word + return prev_block_res; + } + WERD_RES *word() { //current word + return word_res; + } + ROW_RES *row() { //row of current word + return row_res; + } + BLOCK_RES *block() { //block of cur. word + return block_res; + } + WERD_RES *next_word() { //next word + return next_word_res; + } + ROW_RES *next_row() { //row of next word + return next_row_res; + } + BLOCK_RES *next_block() { //block of next word + return next_block_res; + } + void rej_stat_word(); //for page/block/row + + private: + WERD_RES * prev_word_res; //previous word + ROW_RES *prev_row_res; //row of prev word + BLOCK_RES *prev_block_res; //block of prev word + + WERD_RES *word_res; //current word + ROW_RES *row_res; //row of current word + BLOCK_RES *block_res; //block of cur. word + + WERD_RES *next_word_res; //next word + ROW_RES *next_row_res; //row of next word + BLOCK_RES *next_block_res; //block of next word + + BLOCK_RES_IT block_res_it; //iterators + ROW_RES_IT row_res_it; + WERD_RES_IT word_res_it; +}; +#endif diff --git a/ccstruct/pdblock.cpp b/ccstruct/pdblock.cpp new file mode 100644 index 0000000000..182163aedb --- /dev/null +++ b/ccstruct/pdblock.cpp @@ -0,0 +1,363 @@ +/********************************************************************** + * File: pdblock.c (Formerly pdblk.c) + * Description: PDBLK member functions and iterator functions. + * Author: Ray Smith + * Created: Fri Mar 15 09:41:28 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include "blckerr.h" +#include "pdblock.h" +#include "showim.h" + +#include "hpddef.h" //must be last (handpd.dll) + +#define BLOCK_LABEL_HEIGHT 150 //char height of block id + +CLISTIZE (PDBLK) +/********************************************************************** + * PDBLK::PDBLK + * + * Constructor for a simple rectangular block. + **********************************************************************/ +PDBLK::PDBLK ( //rectangular block +INT16 xmin, //bottom left +INT16 ymin, INT16 xmax, //top right +INT16 ymax): box (ICOORD (xmin, ymin), ICOORD (xmax, ymax)) { + //boundaries + ICOORDELT_IT left_it = &leftside; + ICOORDELT_IT right_it = &rightside; + + hand_block = NULL; + hand_poly = NULL; + left_it.set_to_list (&leftside); + right_it.set_to_list (&rightside); + //make default box + left_it.add_to_end (new ICOORDELT (xmin, ymin)); + left_it.add_to_end (new ICOORDELT (xmin, ymax)); + right_it.add_to_end (new ICOORDELT (xmax, ymin)); + right_it.add_to_end (new ICOORDELT (xmax, ymax)); +} + + +/********************************************************************** + * PDBLK::set_sides + * + * Sets left and right vertex lists + **********************************************************************/ + +void PDBLK::set_sides( //set vertex lists + ICOORDELT_LIST *left, //left vertices + ICOORDELT_LIST *right //right vertices + ) { + //boundaries + ICOORDELT_IT left_it = &leftside; + ICOORDELT_IT right_it = &rightside; + + leftside.clear (); + left_it.move_to_first (); + left_it.add_list_before (left); + rightside.clear (); + right_it.move_to_first (); + right_it.add_list_before (right); +} + + +/********************************************************************** + * PDBLK::contains + * + * Return TRUE if the given point is within the block. + **********************************************************************/ + +BOOL8 PDBLK::contains( //test containment + ICOORD pt //point to test + ) { + BLOCK_RECT_IT it = this; //rectangle iterator + ICOORD bleft, tright; //corners of rectangle + + for (it.start_block (); !it.cycled_rects (); it.forward ()) { + //get rectangle + it.bounding_box (bleft, tright); + //inside rect + if (pt.x () >= bleft.x () && pt.x () <= tright.x () + && pt.y () >= bleft.y () && pt.y () <= tright.y ()) + return TRUE; //is inside + } + return FALSE; //not inside +} + + +/********************************************************************** + * PDBLK::move + * + * Reposition block + **********************************************************************/ + +void PDBLK::move( // reposition block + const ICOORD vec // by vector + ) { + ICOORDELT_IT it(&leftside); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + *(it.data ()) += vec; + + it.set_to_list (&rightside); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + *(it.data ()) += vec; + + box.move (vec); +} + + +/********************************************************************** + * PDBLK::plot + * + * Plot the outline of a block in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void PDBLK::plot( //draw outline + WINDOW window, //window to draw in + INT32 serial, //serial number + COLOUR colour //colour to draw in + ) { + ICOORD startpt; //start of outline + ICOORD endpt; //end of outline + ICOORD prevpt; //previous point + ICOORDELT_IT it = &leftside; //iterator + + //set the colour + line_color_index(window, colour); + text_color_index(window, colour); + character_height (window, (float) BLOCK_LABEL_HEIGHT); + text_font_index (window, 6); + + if (!leftside.empty ()) { + startpt = *(it.data ()); //bottom left corner + // tprintf("Block %d bottom left is (%d,%d)\n", + // serial,startpt.x(),startpt.y()); + char temp_buff[34]; + #ifdef __UNIX__ + sprintf(temp_buff, INT32FORMAT, serial); + #else + ultoa (serial, temp_buff, 10); + #endif + text2d (window, startpt.x (), startpt.y (), temp_buff, 0, FALSE); + + move2d (window, startpt.x (), startpt.y ()); + do { + prevpt = *(it.data ()); //previous point + it.forward (); //move to next point + //draw round corner + draw2d (window, prevpt.x (), it.data ()->y ()); + draw2d (window, it.data ()->x (), it.data ()->y ()); + } + while (!it.at_last ()); //until end of list + endpt = *(it.data ()); //end point + + //other side of boundary + move2d (window, startpt.x (), startpt.y ()); + it.set_to_list (&rightside); + prevpt = startpt; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + //draw round corner + draw2d (window, prevpt.x (), it.data ()->y ()); + draw2d (window, it.data ()->x (), it.data ()->y ()); + prevpt = *(it.data ()); //previous point + } + //close boundary + draw2d (window, endpt.x (), endpt.y ()); + if (hand_block != NULL) + hand_block->plot (window, colour, serial); + } +} +#endif + + +/********************************************************************** + * PDBLK::show + * + * Show the image corresponding to a block as its set of rectangles. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void PDBLK::show( //show image block + IMAGE *image, //image to show + WINDOW window //window to show in + ) { + BLOCK_RECT_IT it = this; //rectangle iterator + ICOORD bleft, tright; //corners of rectangle + + for (it.start_block (); !it.cycled_rects (); it.forward ()) { + //get rectangle + it.bounding_box (bleft, tright); + // tprintf("Drawing a block with a bottom left of (%d,%d)\n", + // bleft.x(),bleft.y()); + //show it + show_sub_image (image, bleft.x (), bleft.y (), tright.x () - bleft.x (), tright.y () - bleft.y (), window, bleft.x (), bleft.y ()); + } +} +#endif + + +/********************************************************************** + * PDBLK::operator= + * + * Assignment - duplicate the block structure, but with an EMPTY row list. + **********************************************************************/ + +PDBLK & PDBLK::operator= ( //assignment +const PDBLK & source //from this +) { + // this->ELIST_LINK::operator=(source); + if (!leftside.empty ()) + leftside.clear (); + if (!rightside.empty ()) + rightside.clear (); + leftside.deep_copy (&source.leftside); + rightside.deep_copy (&source.rightside); + box = source.box; + return *this; +} + + +/********************************************************************** + * BLOCK_RECT_IT::BLOCK_RECT_IT + * + * Construct a block rectangle iterator. + **********************************************************************/ + +BLOCK_RECT_IT::BLOCK_RECT_IT ( +//iterate rectangles +PDBLK * blkptr //from block +):left_it (&blkptr->leftside), right_it (&blkptr->rightside) { + block = blkptr; //remember block + //non empty list + if (!blkptr->leftside.empty ()) { + start_block(); //ready for iteration + } +} + + +/********************************************************************** + * BLOCK_RECT_IT::set_to_block + * + * Start a new block. + **********************************************************************/ + +void BLOCK_RECT_IT::set_to_block( //start (new) block + PDBLK *blkptr) { //block to start + block = blkptr; //remember block + //set iterators + left_it.set_to_list (&blkptr->leftside); + right_it.set_to_list (&blkptr->rightside); + if (!blkptr->leftside.empty ()) + start_block(); //ready for iteration +} + + +/********************************************************************** + * BLOCK_RECT_IT::start_block + * + * Restart a block. + **********************************************************************/ + +void BLOCK_RECT_IT::start_block() { //start (new) block + left_it.move_to_first (); + right_it.move_to_first (); + left_it.mark_cycle_pt (); + right_it.mark_cycle_pt (); + ymin = left_it.data ()->y (); //bottom of first box + ymax = left_it.data_relative (1)->y (); + if (right_it.data_relative (1)->y () < ymax) + //smallest step + ymax = right_it.data_relative (1)->y (); +} + + +/********************************************************************** + * BLOCK_RECT_IT::forward + * + * Move to the next rectangle in the block. + **********************************************************************/ + +void BLOCK_RECT_IT::forward() { //next rectangle + if (!left_it.empty ()) { //non-empty list + if (left_it.data_relative (1)->y () == ymax) + left_it.forward (); //move to meet top + if (right_it.data_relative (1)->y () == ymax) + right_it.forward (); + //last is special + if (left_it.at_last () || right_it.at_last ()) { + left_it.move_to_first (); //restart + right_it.move_to_first (); + //now at bottom + ymin = left_it.data ()->y (); + } + else { + ymin = ymax; //new bottom + } + //next point + ymax = left_it.data_relative (1)->y (); + if (right_it.data_relative (1)->y () < ymax) + //least step forward + ymax = right_it.data_relative (1)->y (); + } +} + + +/********************************************************************** + * BLOCK_LINE_IT::get_line + * + * Get the the start and width of a line in the block. + **********************************************************************/ + +INT16 BLOCK_LINE_IT::get_line( //get a line + INT16 y, //line to get + INT16 &xext //output extent + ) { + ICOORD bleft; //bounding box + ICOORD tright; //of block & rect + + //get block box + block->bounding_box (bleft, tright); + if (y < bleft.y () || y >= tright.y ()) { + // block->print(stderr,FALSE); + BADBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); + } + + //get rectangle box + rect_it.bounding_box (bleft, tright); + //inside rectangle + if (y >= bleft.y () && y < tright.y ()) { + //width of line + xext = tright.x () - bleft.x (); + return bleft.x (); //start of line + } + for (rect_it.start_block (); !rect_it.cycled_rects (); rect_it.forward ()) { + //get rectangle box + rect_it.bounding_box (bleft, tright); + //inside rectangle + if (y >= bleft.y () && y < tright.y ()) { + //width of line + xext = tright.x () - bleft.x (); + return bleft.x (); //start of line + } + } + LOSTBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); + return 0; //dummy to stop warning +} diff --git a/ccstruct/pdblock.h b/ccstruct/pdblock.h new file mode 100644 index 0000000000..a5f1473abe --- /dev/null +++ b/ccstruct/pdblock.h @@ -0,0 +1,181 @@ +/********************************************************************** + * File: pdblock.h (Formerly pdblk.h) + * Description: Page block class definition. + * Author: Ray Smith + * Created: Thu Mar 14 17:32:01 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef PDBLOCK_H +#define PDBLOCK_H + +#include "img.h" +#include "strngs.h" +#include "pageblk.h" + +#include "hpddef.h" //must be last (handpd.dll) + +class DLLSYM PDBLK; //forward decl + +CLISTIZEH (PDBLK) +class DLLSYM PDBLK //page block +{ + friend class BLOCK_RECT_IT; //block iterator + + //block label + friend void scan_hpd_blocks(const char *name, + PAGE_BLOCK_LIST *page_blocks, //head of full pag + INT32 &block_no, //no of blocks + PDBLK_C_IT *block_it); + friend BOOL8 read_vec_file( //read uscan output + STRING name, //basename of file + INT32 xsize, //page size //output list + INT32 ysize, + PDBLK_CLIST *blocks); + friend BOOL8 read_pd_file( //read uscan output + STRING name, //basename of file + INT32 xsize, //page size //output list + INT32 ysize, + PDBLK_CLIST *blocks); + + public: + PDBLK() { //empty constructor + hand_block = NULL; + hand_poly = NULL; + } + PDBLK( //simple constructor + INT16 xmin, //bottom left + INT16 ymin, + INT16 xmax, //top right + INT16 ymax); + + void set_sides( //set vertex lists + ICOORDELT_LIST *left, //list of left vertices + ICOORDELT_LIST *right); //list of right vertices + + ~PDBLK () { //destructor + } + + TEXT_REGION *text_region() { + return hand_block; + } + POLY_BLOCK *poly_block() { + return hand_poly; + } + void set_poly_block( //set the poly block + POLY_BLOCK *blk) { + hand_poly = blk; + } + void bounding_box( //get box + ICOORD &bottom_left, //bottom left + ICOORD &top_right) const { //topright + bottom_left = box.botleft (); + top_right = box.topright (); + } + //get real box + const BOX &bounding_box() const { + return box; + } + + BOOL8 contains( //is pt inside block + ICOORD pt); + + void move( // reposition block + const ICOORD vec); // by vector + + void plot( //draw histogram + WINDOW window, //window to draw in + INT32 serial, //serial number + COLOUR colour); //colour to draw in + + void show( //show image + IMAGE *image, //image to show + WINDOW window); //window to show in + + PDBLK & operator= ( //assignment + const PDBLK & source); //from this + + protected: + TEXT_REGION * hand_block; //if it exists + POLY_BLOCK *hand_poly; //wierd as well + ICOORDELT_LIST leftside; //left side vertices + ICOORDELT_LIST rightside; //right side vertices + BOX box; //bounding box +}; + +class DLLSYM BLOCK_RECT_IT //rectangle iterator +{ + public: + BLOCK_RECT_IT( //constructor + PDBLK *blkptr); //block to iterate + + //start (new) block + NEWDELETE2 (BLOCK_RECT_IT) void set_to_block ( + PDBLK * blkptr); //block to iterate + + void start_block(); //start iteration + + void forward(); //next rectangle + + BOOL8 cycled_rects() { //test end + return left_it.cycled_list () && right_it.cycled_list (); + } + + void bounding_box( //current rectangle + ICOORD &bleft, //bottom left + ICOORD &tright) { //top right + //bottom left + bleft = ICOORD (left_it.data ()->x (), ymin); + //top right + tright = ICOORD (right_it.data ()->x (), ymax); + } + + private: + INT16 ymin; //bottom of rectangle + INT16 ymax; //top of rectangle + PDBLK *block; //block to iterate + ICOORDELT_IT left_it; //boundary iterators + ICOORDELT_IT right_it; +}; + +class DLLSYM BLOCK_LINE_IT //rectangle iterator +{ + public: + BLOCK_LINE_IT ( //constructor + PDBLK * blkptr) //from block + :rect_it (blkptr) { + block = blkptr; //remember block + } + + //start (new) block + NEWDELETE2 (BLOCK_LINE_IT) void set_to_block ( + PDBLK * blkptr) { //block to start + block = blkptr; //remember block + //set iterator + rect_it.set_to_block (blkptr); + } + + INT16 get_line( //get a line + INT16 y, //line to get + INT16 &xext); //output extent + + private: + PDBLK * block; //block to iterate + BLOCK_RECT_IT rect_it; //rectangle iterator +}; + +int decreasing_top_order( // + const void *row1, + const void *row2); +#endif diff --git a/ccstruct/pdclass.h b/ccstruct/pdclass.h new file mode 100644 index 0000000000..db5feda026 --- /dev/null +++ b/ccstruct/pdclass.h @@ -0,0 +1,54 @@ +/********************************************************************** + * File: pdclass.h (Formerly pdstruct.h) + * Description: Data structures for read_vec_file. + * Author: Ray Smith + * Created: Tue Nov 3 11:42:08 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef PDCLASS_H +#define PDCLASS_H + +#include "points.h" + +struct VEC_HEADER +{ + INT32 filesize; //bytes in file + INT16 bytesize; //sizeof a byte + INT16 arraysize; //no of blocks + INT16 width; //of image + INT16 height; + INT16 res; //not set + INT16 bpp; +}; + +struct BLOCK_HEADER +{ + UINT8 type; //block type + UINT8 valid; //useable flag + UINT8 charsize; //blob size + UINT8 downsamplerate; //?? + UINT8 subtype; //?? + UINT8 temp; //?? + UINT16 offset; //index in vectors + UINT16 order; //block number + UINT16 entries; //no of vectors +}; + +struct VEC_ENTRY +{ + ICOORD start; //start of vector + ICOORD end; //in clockwise dir +}; +#endif diff --git a/ccstruct/points.cpp b/ccstruct/points.cpp new file mode 100644 index 0000000000..e884bfef55 --- /dev/null +++ b/ccstruct/points.cpp @@ -0,0 +1,64 @@ +/********************************************************************** + * File: points.c (Formerly coords.c) + * Description: Member functions for coordinate classes. + * Author: Ray Smith + * Created: Fri Mar 15 08:58:17 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include "serialis.h" +#include "points.h" + +ELISTIZE_S (ICOORDELT) //turn to list +bool FCOORD::normalise() { //Convert to unit vec + float len = length (); + + if (len < 0.0000000001) { + return false; + } + xcoord /= len; + ycoord /= len; + return true; +} + + +void ICOORD::serialise_asc( //convert to ascii + FILE *f //file to write + ) { + serialise_INT32(f, xcoord); + serialise_INT32(f, ycoord); +} + + +void ICOORD::de_serialise_asc( //convert from ascii + FILE *f //file to write + ) { + xcoord = (INT16) de_serialise_INT32 (f); + ycoord = (INT16) de_serialise_INT32 (f); +} + + +void ICOORDELT::serialise_asc( //convert to ascii + FILE *f //file to write + ) { + ((ICOORD *) this)->serialise_asc (f); +} + + +void ICOORDELT::de_serialise_asc( //convert from ascii + FILE *f //file to write + ) { + ((ICOORD *) this)->de_serialise_asc (f); +} diff --git a/ccstruct/points.h b/ccstruct/points.h new file mode 100644 index 0000000000..6eea0bf1a1 --- /dev/null +++ b/ccstruct/points.h @@ -0,0 +1,284 @@ +/********************************************************************** + * File: points.h (Formerly coords.h) + * Description: Coordinate class definitions. + * Author: Ray Smith + * Created: Fri Mar 15 08:32:45 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef POINTS_H +#define POINTS_H + +#include +#include +#include "elst.h" +//#include "ipeerr.h" + +class FCOORD; + +class DLLSYM ICOORD //integer coordinate +{ + friend class FCOORD; + + public: + ICOORD() { //empty constructor + xcoord = ycoord = 0; //default zero + } + ICOORD( //constructor + INT16 xin, //x value + INT16 yin) { //y value + xcoord = xin; + ycoord = yin; + } + ~ICOORD () { //destructor + } + + //access function + NEWDELETE2 (ICOORD) INT16 x () const + { + return xcoord; + } + INT16 y() const { //access_function + return ycoord; + } + + void set_x( //rewrite function + INT16 xin) { + xcoord = xin; //write new value + } + void set_y( //rewrite function + INT16 yin) { //value to set + ycoord = yin; + } + + float sqlength() const { //find sq length + return (float) (xcoord * xcoord + ycoord * ycoord); + } + + float length() const { //find length + return (float) sqrt (sqlength ()); + } + + float pt_to_pt_sqdist( //sq dist between pts + const ICOORD &pt) const { + ICOORD gap; + + gap.xcoord = xcoord - pt.xcoord; + gap.ycoord = ycoord - pt.ycoord; + return gap.sqlength (); + } + + float pt_to_pt_dist( //Distance between pts + const ICOORD &pt) const { + return (float) sqrt (pt_to_pt_sqdist (pt)); + } + + float angle() const { //find angle + return (float) atan2 ((double) ycoord, (double) xcoord); + } + + BOOL8 operator== ( //test equality + const ICOORD & other) { + return xcoord == other.xcoord && ycoord == other.ycoord; + } + BOOL8 operator!= ( //test inequality + const ICOORD & other) { + return xcoord != other.xcoord || ycoord != other.ycoord; + } + friend ICOORD operator! ( //rotate 90 deg anti + const ICOORD &); + friend ICOORD operator- ( //unary minus + const ICOORD &); + friend ICOORD operator+ ( //add + const ICOORD &, const ICOORD &); + friend ICOORD & operator+= ( //add + ICOORD &, const ICOORD &); + friend ICOORD operator- ( //subtract + const ICOORD &, const ICOORD &); + friend ICOORD & operator-= ( //subtract + ICOORD &, const ICOORD &); + friend INT32 operator% ( //scalar product + const ICOORD &, const ICOORD &); + friend INT32 operator *( //cross product + const ICOORD &, + const ICOORD &); + friend ICOORD operator *( //multiply + const ICOORD &, + INT16); + friend ICOORD operator *( //multiply + INT16, + const ICOORD &); + friend ICOORD & operator*= ( //multiply + ICOORD &, INT16); + friend ICOORD operator/ ( //divide + const ICOORD &, INT16); + //divide + friend ICOORD & operator/= (ICOORD &, INT16); + void rotate( //rotate + const FCOORD& vec); //by vector + + void serialise_asc( //serialise to ascii + FILE *f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + protected: + INT16 xcoord; //x value + INT16 ycoord; //y value +}; + +class DLLSYM ICOORDELT:public ELIST_LINK, public ICOORD + //embedded coord list +{ + public: + ICOORDELT() { //empty constructor + } + ICOORDELT ( //constructor + //from ICOORD + ICOORD icoord):ICOORD (icoord) { + } + ICOORDELT( //constructor + INT16 xin, //x value + INT16 yin) { //y value + xcoord = xin; + ycoord = yin; + } + + /* Note that prep_serialise() dump() and de_dump() dont need to do anything + more than terminate recursion. */ + + void prep_serialise() const { //set ptrs to counts + } + + void dump( //write external bits + FILE *) const { + } + + void de_dump( //read external bits + FILE *) { + } + + //serialise to ascii + make_serialise (ICOORDELT) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + +}; + +ELISTIZEH_S (ICOORDELT) +class DLLSYM FCOORD +{ + public: + FCOORD() { + } //empty constructor + FCOORD( //constructor + float xvalue, //coords to set + float yvalue) { + xcoord = xvalue; //set coords + ycoord = yvalue; + } + FCOORD( //make from ICOORD + ICOORD icoord) { //coords to set + xcoord = icoord.xcoord; + ycoord = icoord.ycoord; + } + + float x() const { //get coords + return xcoord; + } + float y() const { + return ycoord; + } + void set_x( //rewrite function + float xin) { + xcoord = xin; //write new value + } + void set_y( //rewrite function + float yin) { //value to set + ycoord = yin; + } + + float sqlength() const { //find sq length + return xcoord * xcoord + ycoord * ycoord; + } + + float length() const { //find length + return (float) sqrt (sqlength ()); + } + + float pt_to_pt_sqdist( //sq dist between pts + const FCOORD &pt) const { + FCOORD gap; + + gap.xcoord = xcoord - pt.xcoord; + gap.ycoord = ycoord - pt.ycoord; + return gap.sqlength (); + } + + float pt_to_pt_dist( //Distance between pts + const FCOORD &pt) const { + return (float) sqrt (pt_to_pt_sqdist (pt)); + } + + float angle() const { //find angle + return (float) atan2 (ycoord, xcoord); + } + + bool normalise(); //Convert to unit vec + + BOOL8 operator== ( //test equality + const FCOORD & other) { + return xcoord == other.xcoord && ycoord == other.ycoord; + } + BOOL8 operator!= ( //test inequality + const FCOORD & other) { + return xcoord != other.xcoord || ycoord != other.ycoord; + } + //rotate 90 deg anti + friend FCOORD operator! (const FCOORD &); + //unary minus + friend FCOORD operator- (const FCOORD &); + //add + friend FCOORD operator+ (const FCOORD &, const FCOORD &); + //add + friend FCOORD & operator+= (FCOORD &, const FCOORD &); + //subtract + friend FCOORD operator- (const FCOORD &, const FCOORD &); + //subtract + friend FCOORD & operator-= (FCOORD &, const FCOORD &); + //scalar product + friend float operator% (const FCOORD &, const FCOORD &); + //cross product + friend float operator *(const FCOORD &, const FCOORD &); + friend FCOORD operator *(const FCOORD &, float); + //multiply + friend FCOORD operator *(float, const FCOORD &); + //multiply + //multiply + friend FCOORD & operator*= (FCOORD &, float); + friend FCOORD operator/ (const FCOORD &, float); + //divide + void rotate( //rotate + const FCOORD vec); //by vector + //divide + friend FCOORD & operator/= (FCOORD &, float); + + private: + float xcoord; //2 floating coords + float ycoord; +}; + +#include "ipoints.h" /*do inline funcs */ +#endif diff --git a/ccstruct/polyaprx.cpp b/ccstruct/polyaprx.cpp new file mode 100644 index 0000000000..b9093af814 --- /dev/null +++ b/ccstruct/polyaprx.cpp @@ -0,0 +1,583 @@ +/********************************************************************** + * File: polyaprx.cpp (Formerly polygon.c) + * Description: Code for polygonal approximation from old edgeprog. + * Author: Ray Smith + * Created: Thu Nov 25 11:42:04 GMT 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#ifdef __UNIX__ +#include +#endif +//#include "edgeloop.h" +#define MAXEDGELENGTH 16000 //must replace +#include "polyaprx.h" +#include "varable.h" +#include "tprintf.h" + +#define EXTERN + +EXTERN BOOL_VAR (poly_debug, FALSE, "Debug old poly"); +EXTERN BOOL_VAR (poly_wide_objects_better, TRUE, +"More accurate approx on wide things"); + +static int par1, par2; + +#define CONVEX 1 /*OUTLINE point is convex */ +#define CONCAVE 2 /*used and set only in edges */ +#define FIXED 4 /*OUTLINE point is fixed */ +#define ONHULL 8 /*on convex hull */ + +#define RUNLENGTH 1 /*length of run */ + +#define DIR 2 /*direction of run */ + +#define CORRECTION 3 /*correction of run */ +//#define MAXSHORT 32767 /*max value of short*/ +#define FLAGS 0 + +#define fixed_dist 20 //really an int_variable +#define approx_dist 15 //really an int_variable + +#define point_diff(p,p1,p2) (p).x = (p1).x - (p2).x ; (p).y = (p1).y - (p2).y +#define CROSS(a,b) ((a).x * (b).y - (a).y * (b).x) +#define LENGTH(a) ((a).x * (a).x + (a).y * (a).y) + +#define DISTANCE(a,b) (((b).x-(a).x) * ((b).x-(a).x) \ + + ((b).y-(a).y) * ((b).y-(a).y)) + +/********************************************************************** + * tesspoly_outline + * + * Approximate an outline from c form using the old tess algorithm. + **********************************************************************/ + +OUTLINE *tesspoly_outline( //old approximation + C_OUTLINE *c_outline, //input + float //xheight + ) { + EDGEPT *edgept; //converted steps + EDGEPT *startpt; //start of outline + BOX loop_box; //bounding box + INT32 area; //loop area + FCOORD pos; //vertex + FCOORD vec; //vector + POLYPT_LIST polypts; //output polygon + POLYPT *polypt; //converted point + POLYPT_IT poly_it = &polypts; //iterator + EDGEPT edgepts[MAXEDGELENGTH]; //converted path + + loop_box = c_outline->bounding_box (); + area = loop_box.height (); + if (!poly_wide_objects_better && loop_box.width () > area) + area = loop_box.width (); + area *= area; + edgept = edgesteps_to_edgepts (c_outline, edgepts); + fix2(edgepts, area); + edgept = poly2 (edgepts, area);/*2nd approximation */ + startpt = edgept; + do { + pos = FCOORD (edgept->pos.x, edgept->pos.y); + vec = FCOORD (edgept->vec.x, edgept->vec.y); + polypt = new POLYPT (pos, vec); + //add to list + poly_it.add_after_then_move (polypt); + edgept = edgept->next; + } + while (edgept != startpt); + if (poly_it.length () <= 2) + return NULL; + else + //turn to outline + return new OUTLINE (&poly_it); +} + + +/********************************************************************** + * edgesteps_to_edgepts + * + * Convert a C_OUTLINE to EDGEPTs. + **********************************************************************/ + +EDGEPT * +edgesteps_to_edgepts ( //convert outline +C_OUTLINE * c_outline, //input +EDGEPT edgepts[] //output is array +) { + INT32 length; //steps in path + ICOORD pos; //current coords + INT32 stepindex; //current step + INT32 stepinc; //increment + INT32 epindex; //current EDGEPT + INT32 count; //repeated steps + ICOORD vec; //for this 8 step + ICOORD prev_vec; + INT8 epdir; //of this step + DIR128 prevdir; //prvious dir + DIR128 dir; //of this step + + pos = c_outline->start_pos (); //start of loop + length = c_outline->pathlength (); + stepindex = 0; + epindex = 0; + prevdir = -1; + count = 0; + do { + dir = c_outline->step_dir (stepindex); + vec = c_outline->step (stepindex); + if (stepindex < length - 1 + && c_outline->step_dir (stepindex + 1) - dir == -32) { + dir += 128 - 16; + vec += c_outline->step (stepindex + 1); + stepinc = 2; + } + else + stepinc = 1; + if (count == 0) { + prevdir = dir; + prev_vec = vec; + } + if (prevdir.get_dir () != dir.get_dir ()) { + edgepts[epindex].pos.x = pos.x (); + edgepts[epindex].pos.y = pos.y (); + prev_vec *= count; + edgepts[epindex].vec.x = prev_vec.x (); + edgepts[epindex].vec.y = prev_vec.y (); + pos += prev_vec; + edgepts[epindex].flags[RUNLENGTH] = count; + edgepts[epindex].prev = &edgepts[epindex - 1]; + edgepts[epindex].flags[FLAGS] = 0; + edgepts[epindex].next = &edgepts[epindex + 1]; + prevdir += 64; + epdir = (DIR128) 0 - prevdir; + epdir >>= 4; + epdir &= 7; + edgepts[epindex].flags[DIR] = epdir; + epindex++; + prevdir = dir; + prev_vec = vec; + count = 1; + } + else + count++; + stepindex += stepinc; + } + while (stepindex < length); + edgepts[epindex].pos.x = pos.x (); + edgepts[epindex].pos.y = pos.y (); + prev_vec *= count; + edgepts[epindex].vec.x = prev_vec.x (); + edgepts[epindex].vec.y = prev_vec.y (); + pos += prev_vec; + edgepts[epindex].flags[RUNLENGTH] = count; + edgepts[epindex].flags[FLAGS] = 0; + edgepts[epindex].prev = &edgepts[epindex - 1]; + edgepts[epindex].next = &edgepts[0]; + prevdir += 64; + epdir = (DIR128) 0 - prevdir; + epdir >>= 4; + epdir &= 7; + edgepts[epindex].flags[DIR] = epdir; + edgepts[0].prev = &edgepts[epindex]; + ASSERT_HOST (pos.x () == c_outline->start_pos ().x () + && pos.y () == c_outline->start_pos ().y ()); + return &edgepts[0]; +} + + +/********************************************************************** + *fix2(start,area) fixes points on the outline according to a trial method* + **********************************************************************/ + +//#pragma OPT_LEVEL 1 /*stop compiler bugs*/ + +void fix2( //polygonal approx + EDGEPT *start, /*loop to approimate */ + int area) { + register EDGEPT *edgept; /*current point */ + register EDGEPT *edgept1; + register EDGEPT *loopstart; /*modified start of loop */ + register EDGEPT *linestart; /*start of line segment */ + register int dir1, dir2; /*directions of line */ + register int sum1, sum2; /*lengths in dir1,dir2 */ + int stopped; /*completed flag */ + int fixed_count; //no of fixed points + int d01, d12, d23, gapmin; + TPOINT d01vec, d12vec, d23vec; + register EDGEPT *edgefix, *startfix; + register EDGEPT *edgefix0, *edgefix1, *edgefix2, *edgefix3; + + edgept = start; /*start of loop */ + while ((edgept->flags[DIR] - edgept->prev->flags[DIR] + 1 & 7) < 3 + && (dir1 = + edgept->prev->flags[DIR] - edgept->next->flags[DIR] & 7) != 2 + && dir1 != 6) + edgept = edgept->next; /*find suitable start */ + loopstart = edgept; /*remember start */ + + stopped = 0; /*not finished yet */ + edgept->flags[FLAGS] |= FIXED; /*fix it */ + do { + linestart = edgept; /*possible start of line */ + dir1 = edgept->flags[DIR]; /*first direction */ + /*length of dir1 */ + sum1 = edgept->flags[RUNLENGTH]; + edgept = edgept->next; + dir2 = edgept->flags[DIR]; /*2nd direction */ + /*length in dir2 */ + sum2 = edgept->flags[RUNLENGTH]; + if ((dir1 - dir2 + 1 & 7) < 3) { + while (edgept->prev->flags[DIR] == edgept->next->flags[DIR]) { + edgept = edgept->next; /*look at next */ + if (edgept->flags[DIR] == dir1) + /*sum lengths */ + sum1 += edgept->flags[RUNLENGTH]; + else + sum2 += edgept->flags[RUNLENGTH]; + } + + if (edgept == loopstart) + stopped = 1; /*finished */ + if (sum2 + sum1 > 2 + && linestart->prev->flags[DIR] == dir2 + && (linestart->prev->flags[RUNLENGTH] > + linestart->flags[RUNLENGTH] || sum2 > sum1)) { + /*start is back one */ + linestart = linestart->prev; + linestart->flags[FLAGS] |= FIXED; + } + + if ((edgept->next->flags[DIR] - edgept->flags[DIR] + 1 & 7) >= 3 + || edgept->flags[DIR] == dir1 && sum1 >= sum2 + || (edgept->prev->flags[RUNLENGTH] < edgept->flags[RUNLENGTH] + || edgept->flags[DIR] == dir2 && sum2 >= sum1) + && linestart->next != edgept) + edgept = edgept->next; + } + /*sharp bend */ + edgept->flags[FLAGS] |= FIXED; + } + /*do whole loop */ + while (edgept != loopstart && !stopped); + + edgept = start; + do { + if (((edgept->flags[RUNLENGTH] >= 8) && + (edgept->flags[DIR] != 2) && (edgept->flags[DIR] != 6)) || + ((edgept->flags[RUNLENGTH] >= 8) && + ((edgept->flags[DIR] == 2) || (edgept->flags[DIR] == 6)))) { + edgept->flags[FLAGS] |= FIXED; + edgept1 = edgept->next; + edgept1->flags[FLAGS] |= FIXED; + } + edgept = edgept->next; + } + while (edgept != start); + + edgept = start; + do { + /*single fixed step */ + if (edgept->flags[FLAGS] & FIXED && edgept->flags[RUNLENGTH] == 1 + /*and neighours free */ + && edgept->next->flags[FLAGS] & FIXED && (edgept->prev->flags[FLAGS] & FIXED) == 0 + /*same pair of dirs */ + && (edgept->next->next->flags[FLAGS] & FIXED) == 0 && edgept->prev->flags[DIR] == edgept->next->flags[DIR] && edgept->prev->prev->flags[DIR] == edgept->next->next->flags[DIR] + && (edgept->prev->flags[DIR] - edgept->flags[DIR] + 1 & 7) < 3) { + /*unfix it */ + edgept->flags[FLAGS] &= ~FIXED; + edgept->next->flags[FLAGS] &= ~FIXED; + } + edgept = edgept->next; /*do all points */ + } + while (edgept != start); /*until finished */ + + stopped = 0; + if (area < 450) + area = 450; + + gapmin = area * fixed_dist * fixed_dist / 44000; + + edgept = start; + fixed_count = 0; + do { + if (edgept->flags[FLAGS] & FIXED) + fixed_count++; + edgept = edgept->next; + } + while (edgept != start); + while ((edgept->flags[FLAGS] & FIXED) == 0) + edgept = edgept->next; + edgefix0 = edgept; + + edgept = edgept->next; + while ((edgept->flags[FLAGS] & FIXED) == 0) + edgept = edgept->next; + edgefix1 = edgept; + + edgept = edgept->next; + while ((edgept->flags[FLAGS] & FIXED) == 0) + edgept = edgept->next; + edgefix2 = edgept; + + edgept = edgept->next; + while ((edgept->flags[FLAGS] & FIXED) == 0) + edgept = edgept->next; + edgefix3 = edgept; + + startfix = edgefix2; + + do { + if (fixed_count <= 3) + break; //already too few + point_diff (d12vec, edgefix1->pos, edgefix2->pos); + d12 = LENGTH (d12vec); + if (d12 <= gapmin) { + point_diff (d01vec, edgefix0->pos, edgefix1->pos); + d01 = LENGTH (d01vec); + point_diff (d23vec, edgefix2->pos, edgefix3->pos); + d23 = LENGTH (d23vec); + if (d01 > d23) { + edgefix2->flags[FLAGS] &= ~FIXED; + fixed_count--; + /* if ( plots[EDGE] & PATHS ) + mark(edgefd,edgefix2->pos.x,edgefix2->pos.y,PLUS); + */ + } + else { + edgefix1->flags[FLAGS] &= ~FIXED; + fixed_count--; + /* if ( plots[EDGE] & PATHS ) + mark(edgefd,edgefix1->pos.x,edgefix1->pos.y,PLUS); + */ + edgefix1 = edgefix2; + } + } + else { + edgefix0 = edgefix1; + edgefix1 = edgefix2; + } + edgefix2 = edgefix3; + edgept = edgept->next; + while ((edgept->flags[FLAGS] & FIXED) == 0) { + if (edgept == startfix) + stopped = 1; + edgept = edgept->next; + } + edgefix3 = edgept; + edgefix = edgefix2; + } + while ((edgefix != startfix) && (!stopped)); +} + + +//#pragma OPT_LEVEL 2 /*stop compiler bugs*/ + +/********************************************************************** + *poly2(startpt,area,path) applies a second approximation to the outline + *using the points which have been fixed by the first approximation* + **********************************************************************/ + +EDGEPT *poly2( //second poly + EDGEPT *startpt, /*start of loop */ + int area /*area of blob box */ + ) { + register EDGEPT *edgept; /*current outline point */ + EDGEPT *loopstart; /*starting point */ + register EDGEPT *linestart; /*start of line */ + register int edgesum; /*correction count */ + + if (area < 1200) + area = 1200; /*minimum value */ + + /*1200(4) */ + par1 = 4500 / (approx_dist * approx_dist); + /*1200(6) */ + par2 = 6750 / (approx_dist * approx_dist); + + loopstart = NULL; /*not found it yet */ + edgept = startpt; /*start of loop */ + + do { + /*current point fixed */ + if (edgept->flags[FLAGS] & FIXED + /*and next not */ + && (edgept->next->flags[FLAGS] & FIXED) == 0) { + loopstart = edgept; /*start of repoly */ + break; + } + edgept = edgept->next; /*next point */ + } + while (edgept != startpt); /*until found or finished */ + + if (loopstart == NULL && (startpt->flags[FLAGS] & FIXED) == 0) { + /*fixed start of loop */ + startpt->flags[FLAGS] |= FIXED; + loopstart = startpt; /*or start of loop */ + } + if (loopstart) { + do { + edgept = loopstart; /*first to do */ + do { + linestart = edgept; + edgesum = 0; /*sum of lengths */ + do { + /*sum lengths */ + edgesum += edgept->flags[RUNLENGTH]; + edgept = edgept->next; /*move on */ + } + while ((edgept->flags[FLAGS] & FIXED) == 0 + && edgept != loopstart && edgesum < 126); + if (poly_debug) + tprintf + ("Poly2:starting at (%d,%d)+%d=(%d,%d),%d to (%d,%d)\n", + linestart->pos.x, linestart->pos.y, linestart->flags[DIR], + linestart->vec.x, linestart->vec.y, edgesum, edgept->pos.x, + edgept->pos.y); + /*reapproximate */ + cutline(linestart, edgept, area); + + while ((edgept->next->flags[FLAGS] & FIXED) + && edgept != loopstart) + edgept = edgept->next; /*look for next non-fixed */ + } + /*do all the loop */ + while (edgept != loopstart); + edgesum = 0; + do { + if (edgept->flags[FLAGS] & FIXED) + edgesum++; + edgept = edgept->next; + } + //count fixed pts + while (edgept != loopstart); + if (edgesum < 3) + area /= 2; //must have 3 pts + } + while (edgesum < 3); + do { + linestart = edgept; + do { + edgept = edgept->next; + } + while ((edgept->flags[FLAGS] & FIXED) == 0); + linestart->next = edgept; + edgept->prev = linestart; + linestart->vec.x = edgept->pos.x - linestart->pos.x; + linestart->vec.y = edgept->pos.y - linestart->pos.y; + } + while (edgept != loopstart); + } + else + edgept = startpt; /*start of loop */ + + loopstart = edgept; /*new start */ + return loopstart; /*correct exit */ +} + + +/********************************************************************** + *cutline(first,last,area) straightens out a line by partitioning + *and joining the ends by a straight line* + **********************************************************************/ + +void cutline( //recursive refine + EDGEPT *first, /*ends of line */ + EDGEPT *last, + int area /*area of object */ + ) { + register EDGEPT *edge; /*current edge */ + TPOINT vecsum; /*vector sum */ + int vlen; /*approx length of vecsum */ + TPOINT vec; /*accumulated vector */ + EDGEPT *maxpoint; /*worst point */ + int maxperp; /*max deviation */ + register int perp; /*perp distance */ + int ptcount; /*no of points */ + int squaresum; /*sum of perps */ + + edge = first; /*start of line */ + if (edge->next == last) + return; /*simple line */ + + /*vector sum */ + vecsum.x = last->pos.x - edge->pos.x; + vecsum.y = last->pos.y - edge->pos.y; + if (vecsum.x == 0 && vecsum.y == 0) { + /*special case */ + vecsum.x = -edge->prev->vec.x; + vecsum.y = -edge->prev->vec.y; + } + /*absolute value */ + vlen = vecsum.x > 0 ? vecsum.x : -vecsum.x; + if (vecsum.y > vlen) + vlen = vecsum.y; /*maximum */ + else if (-vecsum.y > vlen) + vlen = -vecsum.y; /*absolute value */ + + vec.x = edge->vec.x; /*accumulated vector */ + vec.y = edge->vec.y; + maxperp = 0; /*none yet */ + squaresum = ptcount = 0; + edge = edge->next; /*move to actual point */ + maxpoint = edge; /*in case there isn't one */ + do { + perp = CROSS (vec, vecsum); /*get perp distance */ + if (perp != 0) { + perp *= perp; /*squared deviation */ + } + squaresum += perp; /*sum squares */ + ptcount++; /*count points */ + if (poly_debug) + tprintf ("Cutline:Final perp=%d\n", perp); + if (perp > maxperp) { + maxperp = perp; + maxpoint = edge; /*find greatest deviation */ + } + vec.x += edge->vec.x; /*accumulate vectors */ + vec.y += edge->vec.y; + edge = edge->next; + } + while (edge != last); /*test all line */ + + perp = LENGTH (vecsum); + ASSERT_HOST (perp != 0); + + if (maxperp < 256 * MAX_INT16) { + maxperp <<= 8; + maxperp /= perp; /*true max perp */ + } + else { + maxperp /= perp; + maxperp <<= 8; /*avoid overflow */ + } + if (squaresum < 256 * MAX_INT16) + /*mean squared perp */ + perp = (squaresum << 8) / (perp * ptcount); + else + /*avoid overflow */ + perp = (squaresum / perp << 8) / ptcount; + + if (poly_debug) + tprintf ("Cutline:A=%d, max=%.2f(%.2f%%), msd=%.2f(%.2f%%)\n", + area, maxperp / 256.0, maxperp * 200.0 / area, + perp / 256.0, perp * 300.0 / area); + if (maxperp * par1 >= 10 * area || perp * par2 >= 10 * area || vlen >= 126) { + maxpoint->flags[FLAGS] |= FIXED; + /*partitions */ + cutline(first, maxpoint, area); + cutline(maxpoint, last, area); + } +} diff --git a/ccstruct/polyaprx.h b/ccstruct/polyaprx.h new file mode 100644 index 0000000000..6e6feaef57 --- /dev/null +++ b/ccstruct/polyaprx.h @@ -0,0 +1,51 @@ +/********************************************************************** + * File: polyaprx.h (Formerly polygon.h) + * Description: Code for polygonal approximation from old edgeprog. + * Author: Ray Smith + * Created: Thu Nov 25 11:42:04 GMT 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef POLYAPRX_H +#define POLYAPRX_H + +#include "tessclas.h" +#include "poutline.h" +#include "coutln.h" + +OUTLINE *tesspoly_outline( //old approximation + C_OUTLINE *c_outline, //input + float //xheight + ); +EDGEPT *edgesteps_to_edgepts ( //convert outline +C_OUTLINE * c_outline, //input +EDGEPT edgepts[] //output is array +); +void fix2( //polygonal approx + EDGEPT *start, /*loop to approimate */ + int area); +EDGEPT *poly2( //second poly + EDGEPT *startpt, /*start of loop */ + int area /*area of blob box */ + ); +void cutline( //recursive refine + EDGEPT *first, /*ends of line */ + EDGEPT *last, + int area /*area of object */ + ); +#define fixed_dist 20 //really an int_variable +#define point_diff(p,p1,p2) (p).x = (p1).x - (p2).x ; (p).y = (p1).y - (p2).y +#define CROSS(a,b) ((a).x * (b).y - (a).y * (b).x) +#define LENGTH(a) ((a).x * (a).x + (a).y * (a).y) +#endif diff --git a/ccstruct/polyblk.cpp b/ccstruct/polyblk.cpp new file mode 100644 index 0000000000..00a0b94c46 --- /dev/null +++ b/ccstruct/polyblk.cpp @@ -0,0 +1,397 @@ +/********************************************************************** + * File: polyblk.c (Formerly poly_block.c) + * Description: Polygonal blocks + * Author: Sheelagh Lloyd? + * Created: + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include +#include +#include "elst.h" +#include "polyblk.h" + +#include "hpddef.h" //must be last (handpd.dll) + +#define PBLOCK_LABEL_SIZE 150 +#define INTERSECTING MAX_INT16 + +int lessthan(const void *first, const void *second); + +POLY_BLOCK::POLY_BLOCK(ICOORDELT_LIST *points, POLY_TYPE t) { + ICOORDELT_IT v = &vertices; + + vertices.clear (); + v.move_to_first (); + v.add_list_before (points); + compute_bb(); + type = t; +} + + +/********************************************************************** + * POLY_BLOCK::compute_bb + * + * Compute the bounding box from the outline points. + **********************************************************************/ + +void POLY_BLOCK::compute_bb() { //constructor + ICOORD ibl, itr; //integer bb + ICOORD botleft; //bounding box + ICOORD topright; + ICOORD pos; //current pos; + ICOORDELT_IT pts = &vertices; //iterator + + botleft = *pts.data (); + topright = botleft; + do { + pos = *pts.data (); + if (pos.x () < botleft.x ()) + //get bounding box + botleft = ICOORD (pos.x (), botleft.y ()); + if (pos.y () < botleft.y ()) + botleft = ICOORD (botleft.x (), pos.y ()); + if (pos.x () > topright.x ()) + topright = ICOORD (pos.x (), topright.y ()); + if (pos.y () > topright.y ()) + topright = ICOORD (topright.x (), pos.y ()); + pts.forward (); + } + while (!pts.at_first ()); + ibl = ICOORD (botleft.x (), botleft.y ()); + itr = ICOORD (topright.x (), topright.y ()); + box = BOX (ibl, itr); +} + + +/********************************************************************** + * POLY_BLOCK::winding_number + * + * Return the winding number of the outline around the given point. + **********************************************************************/ + +INT16 POLY_BLOCK::winding_number( //winding number + const ICOORD &point //point to wind around + ) { + INT16 count; //winding count + ICOORD pt; //current point + ICOORD vec; //point to current point + ICOORD vvec; //current point to next point + INT32 cross; //cross product + ICOORDELT_IT it = &vertices; //iterator + + count = 0; + do { + pt = *it.data (); + vec = pt - point; + vvec = *it.data_relative (1) - pt; + //crossing the line + if (vec.y () <= 0 && vec.y () + vvec.y () > 0) { + cross = vec * vvec; //cross product + if (cross > 0) + count++; //crossing right half + else if (cross == 0) + return INTERSECTING; //going through point + } + else if (vec.y () > 0 && vec.y () + vvec.y () <= 0) { + cross = vec * vvec; + if (cross < 0) + count--; //crossing back + else if (cross == 0) + return INTERSECTING; //illegal + } + else if (vec.y () == 0 && vec.x () == 0) + return INTERSECTING; + it.forward (); + } + while (!it.at_first ()); + return count; //winding number +} + + +/********************************************************************** + * POLY_BLOCK::contains + * + * Is poly within poly + **********************************************************************/ + +BOOL8 POLY_BLOCK::contains( //other outline + POLY_BLOCK *other) { + INT16 count; //winding count + ICOORDELT_IT it = &vertices; //iterator + ICOORD vertex; + + if (!box.overlap (*(other->bounding_box ()))) + return FALSE; //can't be contained + + /* check that no vertex of this is inside other */ + + do { + vertex = *it.data (); + //get winding number + count = other->winding_number (vertex); + if (count != INTERSECTING) + if (count != 0) + return (FALSE); + it.forward (); + } + while (!it.at_first ()); + + /* check that all vertices of other are inside this */ + + //switch lists + it.set_to_list (other->points ()); + do { + vertex = *it.data (); + //try other way round + count = winding_number (vertex); + if (count != INTERSECTING) + if (count == 0) + return (FALSE); + it.forward (); + } + while (!it.at_first ()); + return TRUE; +} + + +/********************************************************************** + * POLY_BLOCK::rotate + * + * Rotate the POLY_BLOCK. + **********************************************************************/ + +void POLY_BLOCK::rotate( //constructor + FCOORD rotation //cos,sin of angle + ) { + FCOORD pos; //current pos; + ICOORDELT *pt; //current point + ICOORDELT_IT pts = &vertices; //iterator + + do { + pt = pts.data (); + pos.set_x (pt->x ()); + pos.set_y (pt->y ()); + pos.rotate (rotation); + pt->set_x ((INT16) (floor (pos.x () + 0.5))); + pt->set_y ((INT16) (floor (pos.y () + 0.5))); + pts.forward (); + } + while (!pts.at_first ()); + compute_bb(); +} + + +/********************************************************************** + * POLY_BLOCK::move + * + * Move the POLY_BLOCK. + **********************************************************************/ + +void POLY_BLOCK::move( //constructor + ICOORD shift //cos,sin of angle + ) { + ICOORDELT *pt; //current point + ICOORDELT_IT pts = &vertices; //iterator + + do { + pt = pts.data (); + *pt += shift; + pts.forward (); + } + while (!pts.at_first ()); + compute_bb(); +} + + +#ifndef GRAPHICS_DISABLED +void POLY_BLOCK::plot(WINDOW window, COLOUR colour, INT32 num) { + ICOORDELT_IT v = &vertices; + + line_color_index(window, colour); + v.move_to_first (); + + if (num > 0) { + text_color_index(window, colour); + character_height (window, (float) 80); + text_font_index (window, 6); + char temp_buff[34]; + #ifdef __UNIX__ + sprintf(temp_buff, INT32FORMAT, num); + #else + ltoa (num, temp_buff, 10); + #endif + text2d (window, v.data ()->x (), v.data ()->y (), temp_buff, 0, FALSE); + } + move2d (window, v.data ()->x (), v.data ()->y ()); + for (v.mark_cycle_pt (); !v.cycled_list (); v.forward ()) + draw2d (window, v.data ()->x (), v.data ()->y ()); + v.move_to_first (); + draw2d (window, v.data ()->x (), v.data ()->y ()); +} + + +void POLY_BLOCK::fill(WINDOW window, COLOUR colour) { + INT16 y; + INT16 width; + PB_LINE_IT *lines; + ICOORDELT_LIST *segments; + ICOORDELT_IT s_it; + + lines = new PB_LINE_IT (this); + + line_color_index(window, colour); + + for (y = this->bounding_box ()->bottom (); + y <= this->bounding_box ()->top (); y++) { + segments = lines->get_line (y); + if (!segments->empty ()) { + s_it.set_to_list (segments); + for (s_it.mark_cycle_pt (); !s_it.cycled_list (); s_it.forward ()) { + // Note different use of ICOORDELT, x coord is x coord of pixel + // at the start of line segment, y coord is length of line segment + // Last pixel is start pixel + length. + width = s_it.data ()->y (); + move2d (window, s_it.data ()->x (), y); + draw2d (window, s_it.data ()->x () + (float) width, y); + } + } + } +} +#endif + + +BOOL8 POLY_BLOCK::overlap( // do polys overlap + POLY_BLOCK *other) { + INT16 count; //winding count + ICOORDELT_IT it = &vertices; //iterator + ICOORD vertex; + + if (!box.overlap (*(other->bounding_box ()))) + return FALSE; //can't be any overlap + + /* see if a vertex of this is inside other */ + + do { + vertex = *it.data (); + //get winding number + count = other->winding_number (vertex); + if (count != INTERSECTING) + if (count != 0) + return (TRUE); + it.forward (); + } + while (!it.at_first ()); + + /* see if a vertex of other is inside this */ + + //switch lists + it.set_to_list (other->points ()); + do { + vertex = *it.data (); + //try other way round + count = winding_number (vertex); + if (count != INTERSECTING) + if (count != 0) + return (TRUE); + it.forward (); + } + while (!it.at_first ()); + return FALSE; +} + + +ICOORDELT_LIST *PB_LINE_IT::get_line(INT16 y) { + ICOORDELT_IT v, r; + ICOORDELT_LIST *result; + ICOORDELT *x, *current, *previous; + float fy, fx; + + fy = (float) (y + 0.5); + result = new ICOORDELT_LIST (); + r.set_to_list (result); + v.set_to_list (block->points ()); + + for (v.mark_cycle_pt (); !v.cycled_list (); v.forward ()) { + if (((v.data_relative (-1)->y () > y) && (v.data ()->y () <= y)) + || ((v.data_relative (-1)->y () <= y) && (v.data ()->y () > y))) { + previous = v.data_relative (-1); + current = v.data (); + fx = (float) (0.5 + previous->x () + + (current->x () - previous->x ()) * (fy - + previous->y ()) / + (current->y () - previous->y ())); + x = new ICOORDELT ((INT16) fx, 0); + r.add_to_end (x); + } + } + + if (!r.empty ()) { + r.sort (lessthan); + for (r.mark_cycle_pt (); !r.cycled_list (); r.forward ()) + x = r.data (); + for (r.mark_cycle_pt (); !r.cycled_list (); r.forward ()) { + r.data ()->set_y (r.data_relative (1)->x () - r.data ()->x ()); + r.forward (); + delete (r.extract ()); + } + } + + return result; +} + + +int lessthan(const void *first, const void *second) { + ICOORDELT *p1 = (*(ICOORDELT **) first); + ICOORDELT *p2 = (*(ICOORDELT **) second); + + if (p1->x () < p2->x ()) + return (-1); + else if (p1->x () > p2->x ()) + return (1); + else + return (0); +} + + +/********************************************************************** + * POLY_BLOCK::serialise_asc + * + * Converto to ascii file. + **********************************************************************/ + +void POLY_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + vertices.serialise_asc (f); + box.serialise_asc (f); + serialise_INT32(f, type); +} + + +/********************************************************************** + * POLY_BLOCK::de_serialise_asc + * + * Converto from ascii file. + **********************************************************************/ + +void POLY_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + vertices.de_serialise_asc (f); + box.de_serialise_asc (f); + type = (POLY_TYPE) de_serialise_INT32 (f); +} diff --git a/ccstruct/polyblk.h b/ccstruct/polyblk.h new file mode 100644 index 0000000000..6327ab0e3e --- /dev/null +++ b/ccstruct/polyblk.h @@ -0,0 +1,122 @@ +/********************************************************************** + * File: polyblk.h (Formerly poly_block.h) + * Description: Polygonal blocks + * Author: Sheelagh Lloyd? + * Created: + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#ifndef POLYBLK_H +#define POLYBLK_H + +#include "rect.h" +#include "points.h" +#include "grphics.h" +#include "elst.h" + +#include "hpddef.h" //must be last (handpd.dll) + +enum POLY_TYPE +{ + POLY_TEXT, // Text region + POLY_PAGE, // Page block + POLY_X // Don't care +}; + +class DLLSYM POLY_BLOCK //poly block +{ + + public: + POLY_BLOCK() { + } //empty constructor + POLY_BLOCK( //simple constructor + ICOORDELT_LIST *points, + POLY_TYPE t); + ~POLY_BLOCK () { + } //destructor + + BOX *bounding_box() { // access function + return &box; + } + + ICOORDELT_LIST *points() { // access function + return &vertices; + } + + void compute_bb(); + + POLY_TYPE isA() { + return type; + } + + void rotate( //rotate it + FCOORD rotation); + void move( //move it + ICOORD shift); //vector + + void plot(WINDOW window, COLOUR colour, INT32 num); + + void fill(WINDOW window, COLOUR colour); + + BOOL8 contains( // is poly inside poly + POLY_BLOCK *poly); + + BOOL8 overlap( // do polys overlap + POLY_BLOCK *poly); + + INT16 winding_number( // get winding number + const ICOORD &test_pt); // around this point + + void prep_serialise() { + vertices.prep_serialise (); + } + + void dump(FILE *f) { + vertices.dump (f); + } + + void de_dump(FILE *f) { + vertices.de_dump (f); + } + + //convert to ascii + make_serialise (POLY_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //convert from ascii + FILE *f); + + private: + ICOORDELT_LIST vertices; // vertices + BOX box; // bounding box + POLY_TYPE type; // Page block or + // text region +}; + +class DLLSYM PB_LINE_IT //line iterator +{ + public: + PB_LINE_IT( //constructor + POLY_BLOCK *blkptr) { + block = blkptr; + } + + NEWDELETE2 (PB_LINE_IT) void set_to_block (POLY_BLOCK * blkptr) { + block = blkptr; + } + + ICOORDELT_LIST *get_line(INT16 y); + + private: + POLY_BLOCK * block; +}; +#endif diff --git a/ccstruct/polyblob.cpp b/ccstruct/polyblob.cpp new file mode 100644 index 0000000000..a8bc10db39 --- /dev/null +++ b/ccstruct/polyblob.cpp @@ -0,0 +1,344 @@ +/********************************************************************** + * File: polyblob.cpp (Formerly blob.c) + * Description: Code for PBLOB class. + * Author: Ray Smith + * Created: Wed Oct 23 15:17:41 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "varable.h" +#include "ocrrow.h" +#include "polyblob.h" +//#include "lapoly.h" +#include "polyaprx.h" + +#define EXTERN + +EXTERN BOOL_VAR (polygon_tess_approximation, TRUE, +"Do tess poly instead of greyscale"); + +ELISTIZE_S (PBLOB) +/********************************************************************** + * position_outline + * + * Position the outline in the given list at the relevant place + * according to its nesting. + **********************************************************************/ +static void position_outline( //put in place + OUTLINE *outline, //thing to place + OUTLINE_LIST *destlist //desstination list + ) { + OUTLINE *dest_outline; //outline from dest list + OUTLINE_IT it = destlist; //iterator + //iterator on children + OUTLINE_IT child_it = outline->child (); + + if (!it.empty ()) { + do { + dest_outline = it.data (); //get destination + //encloses dest + if (*dest_outline < *outline) { + //take off list + dest_outline = it.extract (); + //put this in place + it.add_after_then_move (outline); + //make it a child + child_it.add_to_end (dest_outline); + while (!it.at_last ()) { + it.forward (); //do rest of list + //check for other children + dest_outline = it.data (); + if (*dest_outline < *outline) { + //take off list + dest_outline = it.extract (); + child_it.add_to_end (dest_outline); + //make it a child + if (it.empty ()) + break; + } + } + return; //finished + } + //enclosed by dest + else if (*outline < *dest_outline) { + position_outline (outline, dest_outline->child ()); + //place in child list + return; //finished + } + it.forward (); + } + while (!it.at_first ()); + } + it.add_to_end (outline); //at outer level +} + + +/********************************************************************** + * plot_outline_list + * + * Draw a list of outlines in the given colour and their children + * in the child colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +static void plot_outline_list( //draw outlines + OUTLINE_LIST *list, //outline to draw + WINDOW window, //window to draw in + COLOUR colour, //colour to use + COLOUR child_colour //colour of children + ) { + OUTLINE *outline; //current outline + OUTLINE_IT it = list; //iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + //draw it + outline->plot (window, colour); + if (!outline->child ()->empty ()) + plot_outline_list (outline->child (), window, + child_colour, child_colour); + } +} +#endif + + +/********************************************************************** + * PBLOB::PBLOB + * + * Constructor to build a PBLOB from a list of OUTLINEs. + * The OUTLINEs are not copied so the source list is emptied. + * The OUTLINEs are nested correctly in the blob. + **********************************************************************/ + +PBLOB::PBLOB( //constructor + OUTLINE_LIST *outline_list //in random order + ) { + OUTLINE *outline; //current outline + OUTLINE_IT it = outline_list; //iterator + + while (!it.empty ()) { //grab the list + outline = it.extract (); //get off the list + //put it in place + position_outline(outline, &outlines); + if (!it.empty ()) + it.forward (); + } +} + + +/********************************************************************** + * approximate_outline_list + * + * Convert a list of outlines to polygonal form. + **********************************************************************/ + +static void approximate_outline_list( //do list of outlines + C_OUTLINE_LIST *srclist, //list to convert + OUTLINE_LIST *destlist, //desstination list + float xheight //height of line + ) { + C_OUTLINE *src_outline; //outline from src list + OUTLINE *dest_outline; //result + C_OUTLINE_IT src_it = srclist; //source iterator + OUTLINE_IT dest_it = destlist; //iterator + + do { + src_outline = src_it.data (); + // if (polygon_tess_approximation) + dest_outline = tesspoly_outline (src_outline, xheight); + // else + // dest_outline=greypoly_outline(src_outline,xheight); + if (dest_outline != NULL) { + dest_it.add_after_then_move (dest_outline); + if (!src_outline->child ()->empty ()) + //do child list + approximate_outline_list (src_outline->child (), dest_outline->child (), xheight); + } + src_it.forward (); + } + while (!src_it.at_first ()); +} + + +/********************************************************************** + * PBLOB::PBLOB + * + * Constructor to build a PBLOB from a C_BLOB by polygonal approximation. + **********************************************************************/ + +PBLOB::PBLOB( //constructor + C_BLOB *cblob, //compact blob + float xheight //height of line + ) { + BOX bbox; //bounding box + + if (!cblob->out_list ()->empty ()) { + //get bounding box + bbox = cblob->bounding_box (); + if (bbox.height () > xheight) + xheight = bbox.height (); //max of line and blob + //copy it + approximate_outline_list (cblob->out_list (), &outlines, xheight); + } +} + + +/********************************************************************** + * PBLOB::bounding_box + * + * Return the bounding box of the blob. + **********************************************************************/ + +BOX PBLOB::bounding_box() { //bounding box + OUTLINE *outline; //current outline + OUTLINE_IT it = &outlines; //outlines of blob + BOX box; //bounding box + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + box += outline->bounding_box (); + } + return box; +} + + +/********************************************************************** + * PBLOB::area + * + * Return the area of the blob. + **********************************************************************/ + +float PBLOB::area() { //area + OUTLINE *outline; //current outline + OUTLINE_IT it = &outlines; //outlines of blob + float total; //total area + + total = 0.0f; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + total += outline->area (); + } + return total; +} + + +/********************************************************************** + * PBLOB::baseline_normalise + * + * Baseline normalize a blob + **********************************************************************/ + +PBLOB *PBLOB::baseline_normalise( //normalize blob + ROW *row, //row it came from + DENORM *denorm //inverse mapping + ) { + BOX blob_box = bounding_box (); + float x_centre = (blob_box.left () + blob_box.right ()) / 2.0; + PBLOB *bn_blob; //copied blob + + *denorm = DENORM (x_centre, bln_x_height / row->x_height (), row); + bn_blob = new PBLOB; //get one + *bn_blob = *this; //deep copy + bn_blob->move (FCOORD (-denorm->origin (), -row->base_line (x_centre))); + bn_blob->scale (denorm->scale ()); + bn_blob->move (FCOORD (0.0, bln_baseline_offset)); + return bn_blob; +} + + +/********************************************************************** + * PBLOB::baseline_denormalise + * + * DeBaseline Normalise the blob properly with the given denorm. + **********************************************************************/ + +void PBLOB::baseline_denormalise( // Tess style BL Norm + const DENORM *denorm //antidote + ) { + float blob_x_left; // Left edge of blob. + BOX blob_box; //blob bounding box + + move(FCOORD (0.0f, 0.0f - bln_baseline_offset)); + blob_box = bounding_box (); + blob_x_left = blob_box.left (); + scale (1.0 / denorm->scale_at_x (blob_x_left)); + move (FCOORD (denorm->origin (), + denorm->yshift_at_x (blob_x_left))); +} + + +/********************************************************************** + * PBLOB::move + * + * Move PBLOB by vector + **********************************************************************/ + +void PBLOB::move( // reposition blob + const FCOORD vec // by vector + ) { + OUTLINE_IT it(&outlines); // iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->move (vec); // move each outline +} + + +/********************************************************************** + * PBLOB::scale + * + * Scale PBLOB by float multiplier + **********************************************************************/ + +void PBLOB::scale( // scale blob + const float f // by multiplier + ) { + OUTLINE_IT it(&outlines); // iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->scale (f); // scale each outline +} + + +/********************************************************************** + * PBLOB::scale + * + * Scale PBLOB by float multiplier + **********************************************************************/ + +void PBLOB::scale( // scale blob + const FCOORD vec // by multiplier + ) { + OUTLINE_IT it(&outlines); // iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->scale (vec); // scale each outline +} + + +/********************************************************************** + * PBLOB::plot + * + * Draw the PBLOB in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void PBLOB::plot( //draw it + WINDOW window, //window to draw in + COLOUR blob_colour, //main colour + COLOUR child_colour //for holes + ) { + plot_outline_list(&outlines, window, blob_colour, child_colour); +} +#endif diff --git a/ccstruct/polyblob.h b/ccstruct/polyblob.h new file mode 100644 index 0000000000..74cec0026b --- /dev/null +++ b/ccstruct/polyblob.h @@ -0,0 +1,94 @@ +/********************************************************************** + * File: polyblob.h (Formerly blob.h) + * Description: Code for PBLOB class. + * Author: Ray Smith + * Created: Wed Oct 23 15:17:41 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef POLYBLOB_H +#define POLYBLOB_H + +#include "poutline.h" +#include "rect.h" +#include "normalis.h" +#include "stepblob.h" + +class PBLOB:public ELIST_LINK +{ + public: + PBLOB() { + } //empty constructor + PBLOB( //constructor + OUTLINE_LIST *outline_list); //in random order + PBLOB( //constructor + C_BLOB *cblob, //polygonal approx + float xheight); + + OUTLINE_LIST *out_list() { //get outline list + return &outlines; + } + + BOX bounding_box(); //compute bounding box + float area(); //get area of blob + + PBLOB *baseline_normalise( //normalise single blob + ROW *row, //row it came from + DENORM *denorm); //inverse mapping out + void baseline_denormalise( //denormalise + const DENORM *denorm); //antidote + + void plot( //draw one + WINDOW window, //window to draw in + COLOUR blob_colour, //for outer bits + COLOUR child_colour); //for holes + + void move( // reposition blob + const FCOORD vec); // by FLOAT vector + + void scale( // scale blob + const float f); // by multiplier + void scale( // scale blob + const FCOORD vec); // by FLOAT vector + + void prep_serialise() { //set ptrs to counts + outlines.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + outlines.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + outlines.de_dump (f); + } + + //assignment + make_serialise (PBLOB) PBLOB & operator= ( + const PBLOB & source) { //from this + if (!outlines.empty ()) + outlines.clear (); + + outlines.deep_copy (&source.outlines); + return *this; + } + + private: + OUTLINE_LIST outlines; //master elements +}; + +ELISTIZEH_S (PBLOB) +#endif diff --git a/ccstruct/polyvert.cpp b/ccstruct/polyvert.cpp new file mode 100644 index 0000000000..cad11add90 --- /dev/null +++ b/ccstruct/polyvert.cpp @@ -0,0 +1,23 @@ +/********************************************************************** + * File: polyvert.cpp (Formerly polypt.c) + * Description: Code for the POLYPT class. + * Author: Ray Smith + * Created: Wed Oct 23 11:02:56 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "polyvert.h" + +ELIST2IZE_S (POLYPT) diff --git a/ccstruct/polyvert.h b/ccstruct/polyvert.h new file mode 100644 index 0000000000..15b0628677 --- /dev/null +++ b/ccstruct/polyvert.h @@ -0,0 +1,52 @@ +/********************************************************************** + * File: polyvert.h (Formerly polypt.h) + * Description: Code for the POLYPT class. + * Author: Ray Smith + * Created: Wed Oct 23 11:02:56 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef POLYVERT_H +#define POLYVERT_H + +#include "elst2.h" +#include "rect.h" + +class POLYPT:public ELIST2_LINK +{ + public: + POLYPT() { //empty + } + POLYPT( //constructor + const FCOORD &position, //coords + const FCOORD &vector) { //step to next + pos = position; + vec = vector; //just copy + } + + void prep_serialise() { //set ptrs to counts + } + void dump( //write external bits + FILE *) { + } + void de_dump( //read external bits + FILE *) { + } + //really simple + make_serialise (POLYPT) FCOORD pos; + FCOORD vec; //vector to next +}; + +ELIST2IZEH_S (POLYPT) +#endif diff --git a/ccstruct/poutline.cpp b/ccstruct/poutline.cpp new file mode 100644 index 0000000000..8a65ec0c5c --- /dev/null +++ b/ccstruct/poutline.cpp @@ -0,0 +1,409 @@ +/********************************************************************** + * File: poutline.cpp (Formerly outline.c) + * Description: Code for OUTLINE class. + * Author: Ray Smith + * Created: Wed Oct 23 10:52:04 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "poutline.h" + +ELISTIZE_S (OUTLINE) +/********************************************************************** + * OUTLINE::OUTLINE + * + * Constructor to build a OUTLINE from a compact LOOP. + **********************************************************************/ +OUTLINE::OUTLINE ( //constructor +const ICOORD & startpt, //start position +INT8 * compactloop, //from Tess format +BOOL8 invert, //reverse it +ICOORD bot_left, //bounding box +ICOORD top_right): +box (bot_left, top_right), +start(startpt) { + ICOORD pos; //current point + ICOORD vec; //vector to next + POLYPT *polypt; //new point + INT8 *vector; //compact loop + POLYPT_IT it = &outline; //iterator + + pos = startpt; + vector = compactloop; + do { + //vector to next + vec = ICOORD (*vector, *(vector + 1)); + //make a new one + polypt = new POLYPT (FCOORD (pos), FCOORD (vec)); + //add to list + it.add_after_then_move (polypt); + pos += vec; //move to next + vector += 2; + } + while (pos != startpt); + if (invert) + reverse(); //now reverse it +} + + +/********************************************************************** + * OUTLINE::OUTLINE + * + * Constructor to build an OUTLINE from a list of POLYPTs. + **********************************************************************/ + +OUTLINE::OUTLINE( //constructor + POLYPT_IT *polypts //input list + ) { + POLYPT_IT other_it = *polypts; //end of list + + polypts->move_to_first (); + other_it.move_to_last (); + //put in outline + outline.assign_to_sublist (polypts, &other_it); + compute_bb(); +} + + +/********************************************************************** + * OUTLINE::compute_bb + * + * Compute the bounding box from the outline points. + **********************************************************************/ + +void OUTLINE::compute_bb() { //constructor + ICOORD ibl, itr; //integer bb + FCOORD botleft; //bounding box + FCOORD topright; + FCOORD pos; //current pos; + POLYPT_IT polypts = &outline; //iterator + + botleft = polypts.data ()->pos; + topright = botleft; + start = ICOORD ((INT16) botleft.x (), (INT16) botleft.y ()); + do { + pos = polypts.data ()->pos; + if (pos.x () < botleft.x ()) + //get bounding box + botleft = FCOORD (pos.x (), botleft.y ()); + if (pos.y () < botleft.y ()) + botleft = FCOORD (botleft.x (), pos.y ()); + if (pos.x () > topright.x ()) + topright = FCOORD (pos.x (), topright.y ()); + if (pos.y () > topright.y ()) + topright = FCOORD (topright.x (), pos.y ()); + polypts.forward (); + } + while (!polypts.at_first ()); + ibl = ICOORD ((INT16) botleft.x (), (INT16) botleft.y ()); + itr = ICOORD ((INT16) topright.x () + 1, (INT16) topright.y () + 1); + box = BOX (ibl, itr); +} + + +/********************************************************************** + * OUTLINE::area + * + * Compute the area from the outline points. + **********************************************************************/ + +float OUTLINE::area() { //constructor + FCOORD origin; //startpt + FCOORD prev_vec; //previous value of vec + FCOORD vec; //from start to current + float total; //total area + POLYPT_IT poly_it = polypts ();//iterator + //child outline itertr + OUTLINE_IT child_it(&children); + + origin = poly_it.data ()->pos; + poly_it.forward (); + vec = poly_it.data ()->pos - origin; + poly_it.forward (); + total = 0.0f; + while (!poly_it.at_first ()) { + prev_vec = vec; + vec = poly_it.data ()->pos - origin; + total += prev_vec * vec; + poly_it.forward (); + } + total /= 2; + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + //add ares of childrein + total += child_it.data ()->area (); + } + return total; +} + + +/********************************************************************** + * OUTLINE::operator< + * + * Return TRUE if the left operand is inside the right one. + **********************************************************************/ + +BOOL8 +OUTLINE::operator< ( //winding number +OUTLINE & other //other outline +) { + INT16 count; //winding count + POLYPT_IT it = &outline; //iterator + + if (!box.overlap (other.box)) + return FALSE; //can't be contained + + do { + count = other.winding_number (FCOORD (it.data ()->pos)); + //get winding number + if (count != INTERSECTING) + return count != 0; + it.forward (); + } + while (!it.at_first ()); + + //switch lists + it.set_to_list (&other.outline); + do { + //try other way round + count = winding_number (FCOORD (it.data ()->pos)); + if (count != INTERSECTING) + return count == 0; + it.forward (); + } + while (!it.at_first ()); + return TRUE; +} + + +/********************************************************************** + * OUTLINE::winding_number + * + * Return the winding number of the outline around the given point. + **********************************************************************/ + +INT16 OUTLINE::winding_number( //winding number + const FCOORD &point //point to wind around + ) { + INT16 count; //winding count + POLYPT *polypt; //current point + FCOORD vec; //to current point + float cross; //cross product + POLYPT_IT it = &outline; //iterator + + count = 0; + do { + polypt = it.data (); + vec = polypt->pos - point; + //crossing the line + if (vec.y () <= 0 && vec.y () + polypt->vec.y () > 0) { + cross = vec * polypt->vec; //cross product + if (cross > 0) + count++; //crossing right half + else if (cross == 0) + return INTERSECTING; //going through point + } + else if (vec.y () > 0 && vec.y () + polypt->vec.y () <= 0) { + cross = vec * polypt->vec; + if (cross < 0) + count--; //crossing back + else if (cross == 0) + return INTERSECTING; //illegal + } + it.forward (); + } + while (!it.at_first ()); + return count; //winding number +} + + +/********************************************************************** + * OUTLINE::reverse + * + * Reverse the direction of an outline. + **********************************************************************/ + +void OUTLINE::reverse() { //reverse direction + POLYPT_LIST back_list; //reversed list + POLYPT_IT dest_it = &back_list;//destination + POLYPT_IT src_it = &outline; //source list + POLYPT *polypt; //current point + + do { + polypt = src_it.extract (); + //copy in reverse + dest_it.add_after_then_move (polypt); + src_it.backward (); + } + while (!src_it.empty ()); + dest_it.move_to_first (); + do { + polypt = dest_it.data (); + polypt->vec = dest_it.data_relative (1)->pos - polypt->pos; + //vector to next + dest_it.forward (); + } + while (!dest_it.at_first ()); + dest_it.backward (); + src_it.set_to_list (&back_list); + //put it back + outline.assign_to_sublist (&src_it, &dest_it); +} + + +/********************************************************************** + * OUTLINE::move + * + * Move OUTLINE by vector + **********************************************************************/ + +void OUTLINE::move( // reposition OUTLINE + const FCOORD vec // by vector + ) { + //child outline itertr + OUTLINE_IT child_it(&children); + POLYPT_IT poly_it(&outline); //outline point itertr + + box.move (vec); + + start.set_x ((INT16) floor (start.x () + vec.x () + 0.5)); + // ?? Why ICOORD? + start.set_y ((INT16) floor (start.y () + vec.y () + 0.5)); + // ?? Why ICOORD? + + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) + poly_it.data ()->pos += vec; + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) + child_it.data ()->move (vec); // move child outlines +} + + +/********************************************************************** + * OUTLINE::scale + * + * Scale OUTLINE by vector + **********************************************************************/ + +void OUTLINE::scale( // scale OUTLINE + const float f // by multiplier + ) { + //child outline itertr + OUTLINE_IT child_it(&children); + POLYPT_IT poly_it(&outline); //outline point itertr + POLYPT *pt; + + box.scale (f); + + // ?? Why ICOORD? + start.set_x ((INT16) floor (start.x () * f + 0.5)); + // ?? Why ICOORD? + start.set_y ((INT16) floor (start.y () * f + 0.5)); + + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) { + pt = poly_it.data (); + pt->pos *= f; + pt->vec *= f; + } + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) + child_it.data ()->scale (f); //scale child outlines +} + + +/********************************************************************** + * OUTLINE::scale + * + * Scale OUTLINE by vector + **********************************************************************/ + +void OUTLINE::scale( // scale OUTLINE + const FCOORD vector //by fcoord + ) { + //child outline itertr + OUTLINE_IT child_it(&children); + POLYPT_IT poly_it(&outline); //outline point itertr + POLYPT *pt; + + box.scale (vector); + + start.set_x ((INT16) floor (start.x () * vector.x () + 0.5)); + // ?? Why ICOORD? + start.set_y ((INT16) floor (start.y () * vector.y () + 0.5)); + // ?? Why ICOORD? + + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) { + pt = poly_it.data (); + pt->pos = + FCOORD (pt->pos.x () * vector.x (), pt->pos.y () * vector.y ()); + pt->vec = + FCOORD (pt->vec.x () * vector.x (), pt->vec.y () * vector.y ()); + } + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) + //scale child outlines + child_it.data ()->scale (vector); +} + + +/********************************************************************** + * OUTLINE::plot + * + * Draw the outline in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void OUTLINE::plot( //draw it + WINDOW window, //window to draw in + COLOUR colour //colour to draw in + ) { + POLYPT *polypt; //current point + POLYPT_IT it = &outline; //iterator + + line_color_index(window, colour); + polypt = it.data (); + move2d (window, polypt->pos.x (), polypt->pos.y ()); + do { + it.forward (); + polypt = it.data (); + draw2d (window, polypt->pos.x (), polypt->pos.y ()); + } + while (!it.at_first ()); +} +#endif + + +/********************************************************************** + * OUTLINE::operator= + * + * Assignment - deep copy data + **********************************************************************/ + +OUTLINE & OUTLINE::operator= ( //assignment +const OUTLINE & source //from this +) { + box = source.box; + start = source.start; + if (!outline.empty ()) + outline.clear (); + outline.deep_copy (&source.outline); + if (!children.empty ()) + children.clear (); + children.deep_copy (&source.children); + return *this; +} diff --git a/ccstruct/poutline.h b/ccstruct/poutline.h new file mode 100644 index 0000000000..6ab98232a5 --- /dev/null +++ b/ccstruct/poutline.h @@ -0,0 +1,115 @@ +/********************************************************************** + * File: poutline.h (Formerly outline.h) + * Description: OUTLINE class definition. + * Author: Ray Smith + * Created: Wed Oct 23 10:42:40 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef POUTLINE_H +#define POUTLINE_H + +#include "grphics.h" +#include "polyvert.h" +#include "rect.h" +#include "blckerr.h" + +#define INTERSECTING MAX_INT16//no winding number + +class OUTLINE; //forward declaration + +ELISTIZEH_S (OUTLINE) +class OUTLINE:public ELIST_LINK +{ + public: + OUTLINE() { //empty constructor + } + OUTLINE( //constructor + const ICOORD &startpt, //start point + INT8 *compactloop, //from Tess format + BOOL8 reverse, //reverse it + ICOORD bot_left, //bounding box + ICOORD top_right); + OUTLINE( //constructor + POLYPT_IT *poly_it); //from list of pts + + OUTLINE_LIST *child() { //get child list + return &children; + } + + //access function + const BOX &bounding_box() const { + return box; + } + void compute_bb(); //set bounding box + + //get start position + const ICOORD &start_pos() const { + return start; + } + float area(); //return area + POLYPT_LIST *polypts() { //get poly + return &outline; + } + + BOOL8 operator< ( //containment test + OUTLINE & other); + BOOL8 operator> ( //containment test + OUTLINE & other) { + return other < *this; //use the < to do it + } + INT16 winding_number( //get winding number + const FCOORD &testpt); //around this point + void reverse(); //reverse it + + void move( // reposition outline + const FCOORD vec); // by FLOAT vector + + void scale( // scale outline + const float f); // by multiplier + void scale( // scale outline + const FCOORD vec); // by FLOAT vector + + void plot( //draw one + WINDOW window, //window to draw in + COLOUR colour); //colour to draw it + + void prep_serialise() { //set ptrs to counts + outline.prep_serialise (); + children.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + outline.dump (f); + children.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + outline.de_dump (f); + children.de_dump (f); + } + + //assignment + make_serialise (OUTLINE) OUTLINE & operator= ( + const OUTLINE & source); //from this + + private: + BOX box; //boudning box + ICOORD start; //start coord + POLYPT_LIST outline; //outline points + OUTLINE_LIST children; //child elements +}; +#endif diff --git a/ccstruct/quadlsq.cpp b/ccstruct/quadlsq.cpp new file mode 100644 index 0000000000..8f744c5463 --- /dev/null +++ b/ccstruct/quadlsq.cpp @@ -0,0 +1,147 @@ +/********************************************************************** + * File: quadlsq.cpp (Formerly qlsq.c) + * Description: Code for least squares approximation of quadratics. + * Author: Ray Smith + * Created: Wed Oct 6 15:14:23 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include +#include "errcode.h" +#include "quadlsq.h" + +const ERRCODE EMPTY_QLSQ = "Can't delete from an empty QLSQ"; + +#define EXTERN + +/********************************************************************** + * QLSQ::clear + * + * Function to initialize a QLSQ. + **********************************************************************/ + +void QLSQ::clear() { //initialize + a = 0; + b = 0; + c = 0; + n = 0; //no elements + sigx = 0; //update accumulators + sigy = 0; + sigxx = 0; + sigxy = 0; + sigyy = 0; + sigxxx = 0; + sigxxy = 0; + sigxxxx = 0; +} + + +/********************************************************************** + * QLSQ::add + * + * Add an element to the accumulator. + **********************************************************************/ + +void QLSQ::add( //add an element + double x, //xcoord + double y //ycoord + ) { + n++; //count elements + sigx += x; //update accumulators + sigy += y; + sigxx += x * x; + sigxy += x * y; + sigyy += y * y; + sigxxx += (long double) x *x * x; + sigxxy += (long double) x *x * y; + sigxxxx += (long double) x *x * x * x; +} + + +/********************************************************************** + * QLSQ::remove + * + * Delete an element from the acculuator. + **********************************************************************/ + +void QLSQ::remove( //delete an element + double x, //xcoord + double y //ycoord + ) { + if (n <= 0) + //illegal + EMPTY_QLSQ.error ("QLSQ::remove", ABORT, NULL); + n--; //count elements + sigx -= x; //update accumulators + sigy -= y; + sigxx -= x * x; + sigxy -= x * y; + sigyy -= y * y; + sigxxx -= (long double) x *x * x; + sigxxy -= (long double) x *x * y; + sigxxxx -= (long double) x *x * x * x; +} + + +/********************************************************************** + * QLSQ::fit + * + * Fit the given degree of polynomial and store the result. + **********************************************************************/ + +void QLSQ::fit( //fit polynomial + int degree //degree to fit + ) { + long double cubetemp; //intermediates + long double squaretemp; + long double top96, bottom96; /*accurate top & bottom */ + + if (n >= 4 && degree >= 2) { + cubetemp = sigxxx * n - (long double) sigxx *sigx; + + top96 = + cubetemp * ((long double) sigxy * n - (long double) sigx * sigy); + + squaretemp = (long double) sigxx *n - (long double) sigx *sigx; + + top96 += squaretemp * ((long double) sigxx * sigy - sigxxy * n); + + bottom96 = cubetemp * cubetemp; + + bottom96 -= squaretemp * (sigxxxx * n - (long double) sigxx * sigxx); + + a = top96 / bottom96; + + top96 = ((long double) sigxx * sigx - sigxxx * n) * a + + (long double) sigxy *n - (long double) sigx *sigy; + bottom96 = (long double) sigxx *n - (long double) sigx *sigx; + b = top96 / bottom96; + + c = (sigy - a * sigxx - b * sigx) / n; + } + else if (n == 0 || degree < 0) { + a = b = c = 0; + } + else { + a = 0; + if (n > 1 && degree > 0) { + b = (sigxy * n - sigx * sigy) / (sigxx * n - sigx * sigx); + } + else + b = 0; + c = (sigy - b * sigx) / n; + } +} diff --git a/ccstruct/quadlsq.h b/ccstruct/quadlsq.h new file mode 100644 index 0000000000..2270369e2d --- /dev/null +++ b/ccstruct/quadlsq.h @@ -0,0 +1,67 @@ +/********************************************************************** + * File: quadlsq.h (Formerly qlsq.h) + * Description: Code for least squares approximation of quadratics. + * Author: Ray Smith + * Created: Wed Oct 6 15:14:23 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef QUADLSQ_H +#define QUADLSQ_H + +#include "points.h" + +class QLSQ +{ + public: + QLSQ() { //constructor + clear(); //set to zeros + } + void clear(); //initialize + + void add( //add element + double x, //coords to add + double y); + void remove( //delete element + double x, //coords to delete + double y); + INT32 count() { //no of elements + return n; + } + + void fit( //fit the given + int degree); //return actual + double get_a() { //get x squard + return a; + } + double get_b() { //get x squard + return b; + } + double get_c() { //get x squard + return c; + } + + private: + INT32 n; //no of elements + double a, b, c; //result + double sigx; //sum of x + double sigy; //sum of y + double sigxx; //sum x squared + double sigxy; //sum of xy + double sigyy; //sum y squared + long double sigxxx; //sum x cubed + long double sigxxy; //sum xsquared y + long double sigxxxx; //sum x fourth +}; +#endif diff --git a/ccstruct/quadratc.cpp b/ccstruct/quadratc.cpp new file mode 100644 index 0000000000..2f10ab2265 --- /dev/null +++ b/ccstruct/quadratc.cpp @@ -0,0 +1,21 @@ +/********************************************************************** + * File: quadratc.cpp (Formerly quadrtic.c) + * Description: Code for the QUAD_COEFFS class. + * Author: Ray Smith + * Created: Tue Oct 08 17:24:40 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "quadratc.h" diff --git a/ccstruct/quadratc.h b/ccstruct/quadratc.h new file mode 100644 index 0000000000..9148f548a0 --- /dev/null +++ b/ccstruct/quadratc.h @@ -0,0 +1,63 @@ +/********************************************************************** + * File: quadratc.h (Formerly quadrtic.h) + * Description: Code for the QUAD_COEFFS class. + * Author: Ray Smith + * Created: Tue Oct 08 17:24:40 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef QUADRATC_H +#define QUADRATC_H + +#include "points.h" + +class QUAD_COEFFS +{ + public: + QUAD_COEFFS() { + } //empty constructor + QUAD_COEFFS( //constructor + double xsq, //coefficients + float x, + float constant) { + a = xsq; + b = x; + c = constant; + } + + float y( //evaluate + float x) const { //at x + return (float) ((a * x + b) * x + c); + } + + void move( // reposition word + ICOORD vec) { // by vector + /************************************************************ + y - q = a (x - p)^2 + b (x - p) + c + y - q = ax^2 - 2apx + ap^2 + bx - bp + c + y = ax^2 + (b - 2ap)x + (c - bp + ap^2 + q) + ************************************************************/ + INT16 p = vec.x (); + INT16 q = vec.y (); + + c = (float) (c - b * p + a * p * p + q); + b = (float) (b - 2 * a * p); + } + + double a; //x squared + float b; //x + float c; //constant + private: +}; +#endif diff --git a/ccstruct/quspline.cpp b/ccstruct/quspline.cpp new file mode 100644 index 0000000000..f84183d0f0 --- /dev/null +++ b/ccstruct/quspline.cpp @@ -0,0 +1,382 @@ +/********************************************************************** + * File: quspline.cpp (Formerly qspline.c) + * Description: Code for the QSPLINE class. + * Author: Ray Smith + * Created: Tue Oct 08 17:16:12 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "memry.h" +#include "quadlsq.h" +#include "quspline.h" + +#define QSPLINE_PRECISION 16 //no of steps to draw + +/********************************************************************** + * QSPLINE::QSPLINE + * + * Constructor to build a QSPLINE given the components used in the old code. + **********************************************************************/ + +QSPLINE::QSPLINE( //constructor + INT32 count, //no of segments + INT32 *xstarts, //start coords + double *coeffs //coefficients + ) { + INT32 index; //segment index + + //get memory + xcoords = (INT32 *) alloc_mem ((count + 1) * sizeof (INT32)); + quadratics = (QUAD_COEFFS *) alloc_mem (count * sizeof (QUAD_COEFFS)); + segments = count; + for (index = 0; index < segments; index++) { + //copy them + xcoords[index] = xstarts[index]; + quadratics[index] = QUAD_COEFFS (coeffs[index * 3], + coeffs[index * 3 + 1], + coeffs[index * 3 + 2]); + } + //right edge + xcoords[index] = xstarts[index]; +} + + +/********************************************************************** + * QSPLINE::QSPLINE + * + * Constructor to build a QSPLINE by appproximation of points. + **********************************************************************/ + +QSPLINE::QSPLINE ( //constructor +int xstarts[], //spline boundaries +int segcount, //no of segments +int xpts[], //points to fit +int ypts[], int pointcount, //no of pts +int degree //fit required +) { + register int pointindex; /*no along text line */ + register int segment; /*segment no */ + INT32 *ptcounts; //no in each segment + QLSQ qlsq; /*accumulator */ + + segments = segcount; + xcoords = (INT32 *) alloc_mem ((segcount + 1) * sizeof (INT32)); + ptcounts = (INT32 *) alloc_mem ((segcount + 1) * sizeof (INT32)); + quadratics = (QUAD_COEFFS *) alloc_mem (segcount * sizeof (QUAD_COEFFS)); + memmove (xcoords, xstarts, (segcount + 1) * sizeof (INT32)); + ptcounts[0] = 0; /*none in any yet */ + for (segment = 0, pointindex = 0; pointindex < pointcount; pointindex++) { + while (segment < segcount && xpts[pointindex] >= xstarts[segment]) { + segment++; /*try next segment */ + /*cumulative counts */ + ptcounts[segment] = ptcounts[segment - 1]; + } + ptcounts[segment]++; /*no in previous partition */ + } + while (segment < segcount) { + segment++; + /*zero the rest */ + ptcounts[segment] = ptcounts[segment - 1]; + } + + for (segment = 0; segment < segcount; segment++) { + qlsq.clear (); + /*first blob */ + pointindex = ptcounts[segment]; + if (pointindex > 0 + && xpts[pointindex] != xpts[pointindex - 1] + && xpts[pointindex] != xstarts[segment]) + qlsq.add (xstarts[segment], + ypts[pointindex - 1] + + (ypts[pointindex] - ypts[pointindex - 1]) + * (xstarts[segment] - xpts[pointindex - 1]) + / (xpts[pointindex] - xpts[pointindex - 1])); + for (; pointindex < ptcounts[segment + 1]; pointindex++) { + qlsq.add (xpts[pointindex], ypts[pointindex]); + } + if (pointindex > 0 && pointindex < pointcount + && xpts[pointindex] != xstarts[segment + 1]) + qlsq.add (xstarts[segment + 1], + ypts[pointindex - 1] + + (ypts[pointindex] - ypts[pointindex - 1]) + * (xstarts[segment + 1] - xpts[pointindex - 1]) + / (xpts[pointindex] - xpts[pointindex - 1])); + qlsq.fit (degree); + quadratics[segment].a = qlsq.get_a (); + quadratics[segment].b = qlsq.get_b (); + quadratics[segment].c = qlsq.get_c (); + } + free_mem(ptcounts); +} + + +/********************************************************************** + * QSPLINE::QSPLINE + * + * Constructor to build a QSPLINE from another. + **********************************************************************/ + +QSPLINE::QSPLINE( //constructor + const QSPLINE &src) { + segments = 0; + xcoords = NULL; + quadratics = NULL; + *this = src; +} + + +/********************************************************************** + * QSPLINE::~QSPLINE + * + * Destroy a QSPLINE. + **********************************************************************/ + +QSPLINE::~QSPLINE ( //constructor +) { + if (xcoords != NULL) { + free_mem(xcoords); + xcoords = NULL; + } + if (quadratics != NULL) { + free_mem(quadratics); + quadratics = NULL; + } +} + + +/********************************************************************** + * QSPLINE::operator= + * + * Copy a QSPLINE + **********************************************************************/ + +QSPLINE & QSPLINE::operator= ( //assignment +const QSPLINE & source) { + if (xcoords != NULL) + free_mem(xcoords); + if (quadratics != NULL) + free_mem(quadratics); + + segments = source.segments; + xcoords = (INT32 *) alloc_mem ((segments + 1) * sizeof (INT32)); + quadratics = (QUAD_COEFFS *) alloc_mem (segments * sizeof (QUAD_COEFFS)); + memmove (xcoords, source.xcoords, (segments + 1) * sizeof (INT32)); + memmove (quadratics, source.quadratics, segments * sizeof (QUAD_COEFFS)); + return *this; +} + + +/********************************************************************** + * QSPLINE::step + * + * Return the total of the step functions between the given coords. + **********************************************************************/ + +double QSPLINE::step( //find step functions + double x1, //between coords + double x2) { + int index1, index2; //indices of coords + double total; /*total steps */ + + index1 = spline_index (x1); + index2 = spline_index (x2); + total = 0; + while (index1 < index2) { + total += + (double) quadratics[index1 + 1].y ((float) xcoords[index1 + 1]); + total -= (double) quadratics[index1].y ((float) xcoords[index1 + 1]); + index1++; /*next segment */ + } + return total; /*total steps */ +} + + +/********************************************************************** + * QSPLINE::y + * + * Return the y value at the given x value. + **********************************************************************/ + +double QSPLINE::y( //evaluate + double x //coord to evaluate at + ) const { + INT32 index; //segment index + + index = spline_index (x); + return quadratics[index].y (x);//in correct segment +} + + +/********************************************************************** + * QSPLINE::spline_index + * + * Return the index to the largest xcoord not greater than x. + **********************************************************************/ + +INT32 QSPLINE::spline_index( //evaluate + double x //coord to evaluate at + ) const { + INT32 index; //segment index + INT32 bottom; //bottom of range + INT32 top; //top of range + + bottom = 0; + top = segments; + while (top - bottom > 1) { + index = (top + bottom) / 2; //centre of range + if (x >= xcoords[index]) + bottom = index; //new min + else + top = index; //new max + } + return bottom; +} + + +/********************************************************************** + * QSPLINE::move + * + * Reposition spline by vector + **********************************************************************/ + +void QSPLINE::move( // reposition spline + ICOORD vec // by vector + ) { + INT32 segment; //index of segment + INT16 x_shift = vec.x (); + + for (segment = 0; segment < segments; segment++) { + xcoords[segment] += x_shift; + quadratics[segment].move (vec); + } + xcoords[segment] += x_shift; +} + + +/********************************************************************** + * QSPLINE::overlap + * + * Return TRUE if spline2 overlaps this by no more than fraction less + * than the bounds of this. + **********************************************************************/ + +BOOL8 QSPLINE::overlap( //test overlap + QSPLINE *spline2, //2 cannot be smaller + double fraction //by more than this + ) { + int leftlimit; /*common left limit */ + int rightlimit; /*common right limit */ + + leftlimit = xcoords[1]; + rightlimit = xcoords[segments - 1]; + /*or too non-overlap */ + if (spline2->segments < 3 || spline2->xcoords[1] > leftlimit + fraction * (rightlimit - leftlimit) + || spline2->xcoords[spline2->segments - 1] < rightlimit + - fraction * (rightlimit - leftlimit)) + return FALSE; + else + return TRUE; +} + + +/********************************************************************** + * extrapolate_spline + * + * Extrapolates the spline linearly using the same gradient as the + * quadratic has at either end. + **********************************************************************/ + +void QSPLINE::extrapolate( //linear extrapolation + double gradient, //gradient to use + int xmin, //new left edge + int xmax //new right edge + ) { + register int segment; /*current segment of spline */ + int dest_segment; //dest index + int *xstarts; //new boundaries + QUAD_COEFFS *quads; //new ones + int increment; //in size + + increment = xmin < xcoords[0] ? 1 : 0; + if (xmax > xcoords[segments]) + increment++; + if (increment == 0) + return; + xstarts = (int *) alloc_mem ((segments + 1 + increment) * sizeof (int)); + quads = + (QUAD_COEFFS *) alloc_mem ((segments + increment) * sizeof (QUAD_COEFFS)); + if (xmin < xcoords[0]) { + xstarts[0] = xmin; + quads[0].a = 0; + quads[0].b = gradient; + quads[0].c = y (xcoords[0]) - quads[0].b * xcoords[0]; + dest_segment = 1; + } + else + dest_segment = 0; + for (segment = 0; segment < segments; segment++) { + xstarts[dest_segment] = xcoords[segment]; + quads[dest_segment] = quadratics[segment]; + dest_segment++; + } + xstarts[dest_segment] = xcoords[segment]; + if (xmax > xcoords[segments]) { + quads[dest_segment].a = 0; + quads[dest_segment].b = gradient; + quads[dest_segment].c = y (xcoords[segments]) + - quads[dest_segment].b * xcoords[segments]; + dest_segment++; + xstarts[dest_segment] = xmax + 1; + } + segments = dest_segment; + free_mem(xcoords); + free_mem(quadratics); + xcoords = (INT32 *) xstarts; + quadratics = quads; +} + + +/********************************************************************** + * QSPLINE::plot + * + * Draw the QSPLINE in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void QSPLINE::plot( //draw it + WINDOW window, //window to draw in + COLOUR colour //colour to draw in + ) const { + INT32 segment; //index of segment + INT16 step; //index of poly piece + double increment; //x increment + double x; //x coord + + line_color_index(window, colour); + for (segment = 0; segment < segments; segment++) { + increment = + (double) (xcoords[segment + 1] - + xcoords[segment]) / QSPLINE_PRECISION; + x = xcoords[segment]; + for (step = 0; step <= QSPLINE_PRECISION; step++) { + if (segment == 0 && step == 0) + move2d (window, x, quadratics[segment].y (x)); + else + draw2d (window, x, quadratics[segment].y (x)); + x += increment; + } + } +} +#endif diff --git a/ccstruct/quspline.h b/ccstruct/quspline.h new file mode 100644 index 0000000000..fc2a7e332b --- /dev/null +++ b/ccstruct/quspline.h @@ -0,0 +1,114 @@ +/********************************************************************** + * File: quspline.h (Formerly qspline.h) + * Description: Code for the QSPLINE class. + * Author: Ray Smith + * Created: Tue Oct 08 17:16:12 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef QUSPLINE_H +#define QUSPLINE_H + +#include "grphics.h" +#include "quadratc.h" +#include "serialis.h" +#include "memry.h" +#include "rect.h" + +class ROW; + +class QSPLINE +{ + friend void make_first_baseline(BOX *, + int, + int *, + int *, + QSPLINE *, + QSPLINE *, + float); + friend void make_holed_baseline(BOX *, int, QSPLINE *, QSPLINE *, float); + friend void tweak_row_baseline(ROW *); + public: + QSPLINE() { //empty constructor + segments = 0; + xcoords = NULL; //everything empty + quadratics = NULL; + } + QSPLINE( //copy constructor + const QSPLINE &src); + QSPLINE( //constructor + INT32 count, //number of segments + INT32 *xstarts, //segment starts + double *coeffs); //coefficients + ~QSPLINE (); //destructor + QSPLINE ( //least squares fit + int xstarts[], //spline boundaries + int segcount, //no of segments + int xcoords[], //points to fit + int ycoords[], int blobcount,//no of coords + int degree); //function + + double step( //step change + double x1, //between coords + double x2); + double y( //evaluate + double x) const; //at x + + void move( // reposition spline + ICOORD vec); // by vector + BOOL8 overlap( //test overlap + QSPLINE *spline2, //2 cannot be smaller + double fraction); //by more than this + void extrapolate( //linear extrapolation + double gradient, //gradient to use + int left, //new left edge + int right); //new right edge + +#ifndef GRAPHICS_DISABLED + void plot( //draw it + WINDOW window, //in window + COLOUR colour) const; //in colour +#endif + + void prep_serialise() { //set ptrs to counts + } //not required + + void dump( //write external bits + FILE *f) { + serialise_bytes (f, (void *) xcoords, (segments + 1) * sizeof (INT32)); + serialise_bytes (f, (void *) quadratics, segments * sizeof (QUAD_COEFFS)); + } + + void de_dump( //read external bits + FILE *f) { + xcoords = (INT32 *) de_serialise_bytes (f, + (segments + 1) * sizeof (INT32)); + quadratics = (QUAD_COEFFS *) de_serialise_bytes (f, + segments * + sizeof (QUAD_COEFFS)); + } + + //assign copy + make_serialise (QSPLINE) QSPLINE & operator= ( + const QSPLINE & source); //from this + + private: + + INT32 spline_index( //binary search + double x) const; //for x + INT32 segments; //no of segments + INT32 *xcoords; //no of coords + QUAD_COEFFS *quadratics; //spline pieces +}; +#endif diff --git a/ccstruct/ratngs.cpp b/ccstruct/ratngs.cpp new file mode 100644 index 0000000000..f06f276c6c --- /dev/null +++ b/ccstruct/ratngs.cpp @@ -0,0 +1,262 @@ +/********************************************************************** + * File: ratngs.cpp (Formerly ratings.c) + * Description: Code to manipulate the BLOB_CHOICE and WERD_CHOICE classes. + * Author: Ray Smith + * Created: Thu Apr 23 13:23:29 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +//#include "ipeerr.h" +#include "callcpp.h" +#include "ratngs.h" +//#include "tordvars.h" +extern FILE *matcher_fp; + +ELISTIZE (BLOB_CHOICE) CLISTIZE (BLOB_CHOICE_LIST) CLISTIZE (WERD_CHOICE) +//extern FILE* matcher_fp; +/********************************************************************** + * BLOB_CHOICE::BLOB_CHOICE + * + * Constructor to build a BLOB_CHOICE from a char, rating and certainty. + **********************************************************************/ +BLOB_CHOICE::BLOB_CHOICE( //constructor + char src_class, //character + float src_rating, //rating + float src_cert, //certainty + INT8 src_config //config (font) + ) { + blob_class = src_class; + blob_rating = src_rating; + blob_certainty = src_cert; //just copy them + blob_config = src_config; +} + + +/********************************************************************** + * WERD_CHOICE::WERD_CHOICE + * + * Constructor to build a WERD_CHOICE from a char, rating and certainty. + **********************************************************************/ + +WERD_CHOICE::WERD_CHOICE ( +//constructor +const char *src_string, //word string +float src_rating, //rating +float src_cert, //certainty +UINT8 src_permuter //permuter code +): +word_string(src_string) { + word_rating = src_rating; + word_certainty = src_cert; + word_permuter = src_permuter; //just copy them +} + + +/********************************************************************** + * WERD_CHOICE::operator+= + * + * Cat a second word rating on the end of this current one. + * The ratings are added and the confidence is the min. + * If the permuters are NOT the same the permuter is set to COMPOUND_PERM + **********************************************************************/ + + //add one on +WERD_CHOICE & WERD_CHOICE::operator+= ( +const WERD_CHOICE & second //second word +) { + if (word_string.length () == 0 || second.word_string.length () == 0) { + word_string = NULL; //make it empty + } + else { + //add ratings + word_rating += second.word_rating; + if (second.word_certainty < word_certainty) + //take min + word_certainty = second.word_certainty; + //cat strings + word_string += second.word_string; + if (second.word_permuter != word_permuter) + word_permuter = COMPOUND_PERM; + } + + return *this; +} + + +/********************************************************************** + * print_ratings_list + * + * Send all the ratings out to the logfile. + **********************************************************************/ + +void print_ratings_list( //print whole list + const char *msg, //intro message + BLOB_CHOICE_LIST *ratings //list of results + ) { + BLOB_CHOICE_IT + c_it = ratings; //iterator + + switch (ratings->length ()) { + case 0: + tprintf ("%s:", msg); + break; + case 1: + tprintf ("%s:%c/%g/%g", msg, + c_it.data ()->char_class (), + c_it.data ()->rating (), c_it.data ()->certainty ()); + break; + case 2: + tprintf ("%s:%c/%g/%g %c/%g/%g", msg, + c_it.data ()->char_class (), + c_it.data ()->rating (), + c_it.data ()->certainty (), + c_it.data_relative (1)->char_class (), + c_it.data_relative (1)->rating (), + c_it.data_relative (1)->certainty ()); + break; + case 3: + tprintf ("%s:%c/%g/%g %c/%g/%g %c/%g/%g", msg, + c_it.data ()->char_class (), + c_it.data ()->rating (), + c_it.data ()->certainty (), + c_it.data_relative (1)->char_class (), + c_it.data_relative (1)->rating (), + c_it.data_relative (1)->certainty (), + c_it.data_relative (2)->char_class (), + c_it.data_relative (2)->rating (), + c_it.data_relative (2)->certainty ()); + break; + case 4: + tprintf ("%s:%c/%g/%g %c/%g/%g %c/%g/%g %c/%g/%g", msg, + c_it.data ()->char_class (), + c_it.data ()->rating (), + c_it.data ()->certainty (), + c_it.data_relative (1)->char_class (), + c_it.data_relative (1)->rating (), + c_it.data_relative (1)->certainty (), + c_it.data_relative (2)->char_class (), + c_it.data_relative (2)->rating (), + c_it.data_relative (2)->certainty (), + c_it.data_relative (3)->char_class (), + c_it.data_relative (3)->rating (), + c_it.data_relative (3)->certainty ()); + break; + default: + tprintf ("%s:%c/%g/%g %c/%g/%g %c/%g/%g %c/%g/%g %c/%g/%g", msg, + c_it.data ()->char_class (), + c_it.data ()->rating (), + c_it.data ()->certainty (), + c_it.data_relative (1)->char_class (), + c_it.data_relative (1)->rating (), + c_it.data_relative (1)->certainty (), + c_it.data_relative (2)->char_class (), + c_it.data_relative (2)->rating (), + c_it.data_relative (2)->certainty (), + c_it.data_relative (3)->char_class (), + c_it.data_relative (3)->rating (), + c_it.data_relative (3)->certainty (), + c_it.data_relative (4)->char_class (), + c_it.data_relative (4)->rating (), + c_it.data_relative (4)->certainty ()); + c_it.forward (); + c_it.forward (); + c_it.forward (); + c_it.forward (); + while (!c_it.at_last ()) { + c_it.forward (); + tprintf ("%c/%g/%g", + c_it.data ()->char_class (), + c_it.data ()->rating (), c_it.data ()->certainty ()); + } + + break; + } +} + + +/********************************************************************** + * print_ratings_info + * + * Send all the ratings out to the logfile. + **********************************************************************/ + +void print_ratings_info( //print summary info + FILE *fp, //file to use + BLOB_CHOICE_LIST *ratings //list of results + ) { + INT32 + index; //to list + INT32 + best_index; //to list + FLOAT32 + best_rat; //rating + FLOAT32 + best_cert; //certainty + char + first_char; //character + FLOAT32 + first_rat; //rating + FLOAT32 + first_cert; //certainty + char + sec_char = 0; //character + FLOAT32 + sec_rat = 0.0f; //rating + FLOAT32 + sec_cert = 0.0f; //certainty + BLOB_CHOICE_IT + c_it = ratings; //iterator + + index = ratings->length (); + if (index > 0) { + first_char = c_it.data ()->char_class (); + first_rat = c_it.data ()->rating (); + first_cert = -c_it.data ()->certainty (); + if (index > 1) { + sec_char = c_it.data_relative (1)->char_class (); + sec_rat = c_it.data_relative (1)->rating (); + sec_cert = -c_it.data_relative (1)->certainty (); + } + else { + sec_char = '~'; + sec_rat = -1; + sec_cert = -1; + } + } + else { + first_char = '~'; + first_rat = -1; + first_cert = -1; + } + best_index = -1; + best_rat = -1; + best_cert = -1; + for (index = 0, c_it.mark_cycle_pt (); !c_it.cycled_list (); + c_it.forward (), index++) { + if (c_it.data ()->char_class () == blob_answer) { + best_index = index; + best_rat = c_it.data ()->rating (); + best_cert = -c_it.data ()->certainty (); + } + } + if (first_char == '\0' || first_char == ' ') + first_char = '~'; + if (sec_char == '\0' || sec_char == ' ') + sec_char = '~'; + fprintf (matcher_fp, + " " INT32FORMAT " " INT32FORMAT " %g %g %c %g %g %c %g %g\n", + ratings->length (), best_index, best_rat, best_cert, first_char, + first_rat, first_cert, sec_char, sec_rat, sec_cert); +} diff --git a/ccstruct/ratngs.h b/ccstruct/ratngs.h new file mode 100644 index 0000000000..6cae9b5877 --- /dev/null +++ b/ccstruct/ratngs.h @@ -0,0 +1,146 @@ +/********************************************************************** + * File: ratngs.h (Formerly ratings.h) + * Description: Definition of the WERD_CHOICE and BLOB_CHOICE classes. + * Author: Ray Smith + * Created: Thu Apr 23 11:40:38 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef RATNGS_H +#define RATNGS_H + +#include "clst.h" +#include "werd.h" +#include "notdll.h" + +class BLOB_CHOICE:public ELIST_LINK +{ + public: + BLOB_CHOICE() { //empty + } + BLOB_CHOICE( //constructor + char src_class, //character + float src_rating, //rating + float src_cert, //certainty + INT8 src_config); //config (font) + + void set_class( //change it + char newchar) { + blob_class = newchar; + } + void set_rating( //change it + float newrat) { + blob_rating = newrat; + } + void set_certainty( //change it + float newrat) { + blob_certainty = newrat; + } + void set_config( //change it + INT8 newfont) { + blob_config = newfont; + } + + char char_class() const { //access function + return blob_class; + } + float rating() const { //access function + return blob_rating; + } + float certainty() const { //access function + return blob_certainty; + } + INT8 config() const { //access function + return blob_config; + } + + NEWDELETE private: + char blob_class; //char code + char blob_config; //char config (font) + INT16 junk2; + float blob_rating; //size related + float blob_certainty; //absolute +}; + + //make them listable +ELISTIZEH (BLOB_CHOICE) CLISTIZEH (BLOB_CHOICE_LIST) +/* permuter codes used in WERD_CHOICEs */ +# +#define MIN_PERM 1 +#define NO_PERM 0 +#define TOP_CHOICE_PERM 1 +#define LOWER_CASE_PERM 2 +#define UPPER_CASE_PERM 3 +#define NUMBER_PERM 4 +#define SYSTEM_DAWG_PERM 5 +#define DOC_DAWG_PERM 6 +#define USER_DAWG_PERM 7 +#define FREQ_DAWG_PERM 8 +#define COMPOUND_PERM 9 +#define MAX_PERM 9 +class +WERD_CHOICE +{ + public: + WERD_CHOICE() { //empty + } + WERD_CHOICE( //constructor + const char *src_string, //word string + float src_rating, //rating + float src_cert, //certainty + UINT8 src_permuter); //permuter code + + //access function + const STRING &string() const { + return word_string; + } + + float rating() const { //access function + return word_rating; + } + float certainty() const { //access function + return word_certainty; + } + UINT8 permuter() const { //access function + return word_permuter; + } + void set_permuter( //Override + UINT8 perm) { + word_permuter = perm; + } + + WERD_CHOICE & operator+= ( //concatanate + const WERD_CHOICE & second);//second on first + + NEWDELETE private: + STRING word_string; //text + float word_rating; //size related + float word_certainty; //absolute + UINT8 word_permuter; //permuter code +}; + +CLISTIZEH (WERD_CHOICE) +void print_ratings_list( //print whole list + const char *msg, //intro message + BLOB_CHOICE_LIST *ratings //list of results + ); +void print_ratings_info( //print summary info + FILE *fp, //file to use + BLOB_CHOICE_LIST *ratings //list of results + ); +typedef void (*POLY_MATCHER) (PBLOB *, PBLOB *, PBLOB *, WERD *, +DENORM *, BLOB_CHOICE_LIST &); +typedef void (*POLY_TESTER) (PBLOB *, DENORM *, BOOL8, char *, INT32, +BLOB_CHOICE_LIST *); +#endif diff --git a/ccstruct/rect.cpp b/ccstruct/rect.cpp new file mode 100644 index 0000000000..afdb4c2266 --- /dev/null +++ b/ccstruct/rect.cpp @@ -0,0 +1,232 @@ +/********************************************************************** + * File: rect.c (Formerly box.c) + * Description: Bounding box class definition. + * Author: Phil Cheatle + * Created: Wed Oct 16 15:18:45 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include "rect.h" + +/********************************************************************** + * BOX::BOX() Constructor from 2 ICOORDS + * + **********************************************************************/ + +BOX::BOX( //construtor + const ICOORD pt1, //one corner + const ICOORD pt2 //the other corner + ) { + if (pt1.x () <= pt2.x ()) { + if (pt1.y () <= pt2.y ()) { + bot_left = pt1; + top_right = pt2; + } + else { + bot_left = ICOORD (pt1.x (), pt2.y ()); + top_right = ICOORD (pt2.x (), pt1.y ()); + } + } + else { + if (pt1.y () <= pt2.y ()) { + bot_left = ICOORD (pt2.x (), pt1.y ()); + top_right = ICOORD (pt1.x (), pt2.y ()); + } + else { + bot_left = pt2; + top_right = pt1; + } + } +} + + +/********************************************************************** + * BOX::intersection() Build the largest box contained in both boxes + * + **********************************************************************/ + +BOX BOX::intersection( //shared area box + const BOX &box) const { + ICOORD bl; //bottom left + ICOORD tr; //top right + + if (overlap (box)) { + if (box.bot_left.x () > bot_left.x ()) + bl.set_x (box.bot_left.x ()); + else + bl.set_x (bot_left.x ()); + + if (box.top_right.x () < top_right.x ()) + tr.set_x (box.top_right.x ()); + else + tr.set_x (top_right.x ()); + + if (box.bot_left.y () > bot_left.y ()) + bl.set_y (box.bot_left.y ()); + else + bl.set_y (bot_left.y ()); + + if (box.top_right.y () < top_right.y ()) + tr.set_y (box.top_right.y ()); + else + tr.set_y (top_right.y ()); + } + else { + bl.set_x (MAX_INT16); + bl.set_y (MAX_INT16); + tr.set_x (-MAX_INT16); + tr.set_y (-MAX_INT16); + } + return BOX (bl, tr); +} + + +/********************************************************************** + * BOX::bounding_union() Build the smallest box containing both boxes + * + **********************************************************************/ + +BOX BOX::bounding_union( //box enclosing both + const BOX &box) const { + ICOORD bl; //bottom left + ICOORD tr; //top right + + if (box.bot_left.x () < bot_left.x ()) + bl.set_x (box.bot_left.x ()); + else + bl.set_x (bot_left.x ()); + + if (box.top_right.x () > top_right.x ()) + tr.set_x (box.top_right.x ()); + else + tr.set_x (top_right.x ()); + + if (box.bot_left.y () < bot_left.y ()) + bl.set_y (box.bot_left.y ()); + else + bl.set_y (bot_left.y ()); + + if (box.top_right.y () > top_right.y ()) + tr.set_y (box.top_right.y ()); + else + tr.set_y (top_right.y ()); + return BOX (bl, tr); +} + + +/********************************************************************** + * BOX::plot() Paint a box using specified settings + * + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void BOX::plot( //paint box + WINDOW fd, //where to paint + INT16 style, //display style + INT16 edged, //show border? + COLOUR fill_colour, //colour for inside + COLOUR border_colour //colour for border + ) const { + interior_style(fd, style, edged); + fill_color_index(fd, fill_colour); + perimeter_color_index(fd, border_colour); + plot(fd); +} +#endif + + +/********************************************************************** + * operator+= + * + * Extend one box to include the other (In place union) + **********************************************************************/ + +DLLSYM BOX & +operator+= ( //bounding bounding bx +BOX & op1, //operands +const BOX & op2) { + if (op2.bot_left.x () < op1.bot_left.x ()) + op1.bot_left.set_x (op2.bot_left.x ()); + + if (op2.top_right.x () > op1.top_right.x ()) + op1.top_right.set_x (op2.top_right.x ()); + + if (op2.bot_left.y () < op1.bot_left.y ()) + op1.bot_left.set_y (op2.bot_left.y ()); + + if (op2.top_right.y () > op1.top_right.y ()) + op1.top_right.set_y (op2.top_right.y ()); + + return op1; +} + + +/********************************************************************** + * operator-= + * + * Reduce one box to intersection with the other (In place intersection) + **********************************************************************/ + +DLLSYM BOX & +operator-= ( //inplace intersection +BOX & op1, //operands +const BOX & op2) { + if (op1.overlap (op2)) { + if (op2.bot_left.x () > op1.bot_left.x ()) + op1.bot_left.set_x (op2.bot_left.x ()); + + if (op2.top_right.x () < op1.top_right.x ()) + op1.top_right.set_x (op2.top_right.x ()); + + if (op2.bot_left.y () > op1.bot_left.y ()) + op1.bot_left.set_y (op2.bot_left.y ()); + + if (op2.top_right.y () < op1.top_right.y ()) + op1.top_right.set_y (op2.top_right.y ()); + } + else { + op1.bot_left.set_x (MAX_INT16); + op1.bot_left.set_y (MAX_INT16); + op1.top_right.set_x (-MAX_INT16); + op1.top_right.set_y (-MAX_INT16); + } + return op1; +} + + +/********************************************************************** + * BOX::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void BOX::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + bot_left.serialise_asc (f); + top_right.serialise_asc (f); +} + + +/********************************************************************** + * BOX::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void BOX::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + bot_left.de_serialise_asc (f); + top_right.de_serialise_asc (f); +} diff --git a/ccstruct/rect.h b/ccstruct/rect.h new file mode 100644 index 0000000000..cb951bbc45 --- /dev/null +++ b/ccstruct/rect.h @@ -0,0 +1,287 @@ +/********************************************************************** + * File: rect.h (Formerly box.h) + * Description: Bounding box class definition. + * Author: Phil Cheatle + * Created: Wed Oct 16 15:18:45 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef RECT_H +#define RECT_H + +#include +#include "points.h" +#include "ndminx.h" +#include "grphics.h" +#include "tprintf.h" + +class DLLSYM BOX //bounding box +{ + public: + BOX (): //empty constructor + bot_left (MAX_INT16, MAX_INT16), top_right (-MAX_INT16, -MAX_INT16) { + } //null box + + BOX( //constructor + const ICOORD pt1, //one corner + const ICOORD pt2); //the other corner + BOX( //box around FCOORD + const FCOORD pt); + + BOOL8 null_box() const { //Is box null + return ((left () > right ()) || (top () < bottom ())); + } + + INT16 top() const { // coord of top + return top_right.y (); + } + + INT16 bottom() const { // coord of bottom + return bot_left.y (); + } + + INT16 left() const { // coord of left + return bot_left.x (); + } + + INT16 right() const { // coord of right + return top_right.x (); + } + + //access function + const ICOORD &botleft() const { + return bot_left; + } + + ICOORD botright() const { // ~ access function + return ICOORD (top_right.x (), bot_left.y ()); + } + + ICOORD topleft() const { // ~ access function + return ICOORD (bot_left.x (), top_right.y ()); + } + + //access function + const ICOORD &topright() const { + return top_right; + } + + INT16 height() const { //how high is it? + if (!null_box ()) + return top_right.y () - bot_left.y (); + else + return 0; + } + + INT16 width() const { //how high is it? + if (!null_box ()) + return top_right.x () - bot_left.x (); + else + return 0; + } + + INT32 area() const { //what is the area? + if (!null_box ()) + return width () * height (); + else + return 0; + } + + void move_bottom_edge( // move one edge + const INT16 y) { // by +/- y + bot_left += ICOORD (0, y); + } + + void move_left_edge( // move one edge + const INT16 x) { // by +/- x + bot_left += ICOORD (x, 0); + } + + void move_right_edge( // move one edge + const INT16 x) { // by +/- x + top_right += ICOORD (x, 0); + } + + void move_top_edge( // move one edge + const INT16 y) { // by +/- y + top_right += ICOORD (0, y); + } + + void move( // move box + const ICOORD vec) { // by vector + bot_left += vec; + top_right += vec; + } + + void move( // move box + const FCOORD vec) { // by float vector + bot_left.set_x ((INT16) floor (bot_left.x () + vec.x ())); + //round left + bot_left.set_y ((INT16) floor (bot_left.y () + vec.y ())); + //round down + + top_right.set_x ((INT16) ceil (top_right.x () + vec.x ())); + //round right + top_right.set_y ((INT16) ceil (top_right.y () + vec.y ())); + //round up + } + + void scale( // scale box + const float f) { // by multiplier + //round left + bot_left.set_x ((INT16) floor (bot_left.x () * f)); + //round down + bot_left.set_y ((INT16) floor (bot_left.y () * f)); + + top_right.set_x ((INT16) ceil (top_right.x () * f)); + //round right + top_right.set_y ((INT16) ceil (top_right.y () * f)); + //round up + } + void scale( // scale box + const FCOORD vec) { // by float vector + bot_left.set_x ((INT16) floor (bot_left.x () * vec.x ())); + bot_left.set_y ((INT16) floor (bot_left.y () * vec.y ())); + top_right.set_x ((INT16) ceil (top_right.x () * vec.x ())); + top_right.set_y ((INT16) ceil (top_right.y () * vec.y ())); + } + + void rotate( //rotate coords + const FCOORD vec) { //by vector + bot_left.rotate (vec); + top_right.rotate (vec); + *this = BOX (bot_left, top_right); + } + + BOOL8 contains( //is pt inside box + const FCOORD pt) const; + + BOOL8 contains( //is box inside box + const BOX &box) const; + + BOOL8 overlap( //do boxes overlap + const BOX &box) const; + + BOOL8 major_overlap( // Do boxes overlap more than half. + const BOX &box) const; + + BOX intersection( //shared area box + const BOX &box) const; + + BOX bounding_union( //box enclosing both + const BOX &box) const; + + void print() { //print + tprintf ("Bounding box=(%d,%d)->(%d,%d)\n", + left (), bottom (), right (), top ()); + } + +#ifndef GRAPHICS_DISABLED + void plot( //use current settings + WINDOW fd) const { //where to paint + rectangle (fd, bot_left.x (), bot_left.y (), top_right.x (), + top_right.y ()); + } + + void plot( //paint box + WINDOW fd, //where to paint + INT16 style, //display style + INT16 edged, //show border? + COLOUR fill_colour, //colour for inside + COLOUR border_colour) const; //colour for border +#endif + + friend DLLSYM BOX & operator+= (BOX &, const BOX &); + //in place union + friend DLLSYM BOX & operator-= (BOX &, const BOX &); + //in place intrsection + + void serialise_asc( //convert to ascii + FILE *f); + void de_serialise_asc( //convert from ascii + FILE *f); + + private: + ICOORD bot_left; //bottom left corner + ICOORD top_right; //top right corner +}; + +/********************************************************************** + * BOX::BOX() Constructor from 1 FCOORD + * + **********************************************************************/ + +inline BOX::BOX( //construtor + const FCOORD pt //floating centre + ) { + bot_left = ICOORD ((INT16) floor (pt.x ()), (INT16) floor (pt.y ())); + top_right = ICOORD ((INT16) ceil (pt.x ()), (INT16) ceil (pt.y ())); +} + + +/********************************************************************** + * BOX::contains() Is point within box + * + **********************************************************************/ + +inline BOOL8 BOX::contains(const FCOORD pt) const { + return ((pt.x () >= bot_left.x ()) && + (pt.x () <= top_right.x ()) && + (pt.y () >= bot_left.y ()) && (pt.y () <= top_right.y ())); +} + + +/********************************************************************** + * BOX::contains() Is box within box + * + **********************************************************************/ + +inline BOOL8 BOX::contains(const BOX &box) const { + return (contains (box.bot_left) && contains (box.top_right)); +} + + +/********************************************************************** + * BOX::overlap() Do two boxes overlap? + * + **********************************************************************/ + +inline BOOL8 BOX::overlap( //do boxes overlap + const BOX &box) const { + return ((box.bot_left.x () <= top_right.x ()) && + (box.top_right.x () >= bot_left.x ()) && + (box.bot_left.y () <= top_right.y ()) && + (box.top_right.y () >= bot_left.y ())); +} + +/********************************************************************** + * BOX::major_overlap() Do two boxes overlap by at least half of the smallest? + * + **********************************************************************/ + +inline BOOL8 BOX::major_overlap( // Do boxes overlap more that half. + const BOX &box) const { + int overlap = MIN(box.top_right.x(), top_right.x()); + overlap -= MAX(box.bot_left.x(), bot_left.x()); + overlap += overlap; + if (overlap < MIN(box.width(), width())) + return false; + overlap = MIN(box.top_right.y(), top_right.y()); + overlap -= MAX(box.bot_left.y(), bot_left.y()); + overlap += overlap; + if (overlap < MIN(box.height(), height())) + return false; + return true; +} +#endif diff --git a/ccstruct/rejctmap.cpp b/ccstruct/rejctmap.cpp new file mode 100644 index 0000000000..60cc230b80 --- /dev/null +++ b/ccstruct/rejctmap.cpp @@ -0,0 +1,545 @@ +/********************************************************************** + * File: rejctmap.cpp (Formerly rejmap.c) + * Description: REJ and REJMAP class functions. + * Author: Phil Cheatle + * Created: Thu Jun 9 13:46:38 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "hosthplb.h" +//#include "basefile.h" +#include "rejctmap.h" +#include "secname.h" + +#define EXTERN + +EXTERN BOOL_VAR (rejword_only_set_if_accepted, TRUE, "Mimic old reject_word"); +EXTERN BOOL_VAR (rejmap_allow_more_good_qual, FALSE, +"Use initial good qual setting"); +EXTERN BOOL_VAR (rej_use_1Il_rej, TRUE, "1Il rejection enabled"); + +BOOL8 REJ::perm_rejected() { //Is char perm reject? + return (flag (R_TESS_FAILURE) || + flag (R_SMALL_XHT) || + flag (R_EDGE_CHAR) || + flag (R_1IL_CONFLICT) || + flag (R_POSTNN_1IL) || + flag (R_REJ_CBLOB) || + flag (R_BAD_REPETITION) || flag (R_MM_REJECT)); +} + + +BOOL8 REJ::rej_before_nn_accept() { + return flag (R_POOR_MATCH) || + flag (R_NOT_TESS_ACCEPTED) || + flag (R_CONTAINS_BLANKS) || flag (R_BAD_PERMUTER); +} + + +BOOL8 REJ::rej_between_nn_and_mm() { + return flag (R_HYPHEN) || + flag (R_DUBIOUS) || + flag (R_NO_ALPHANUMS) || flag (R_MOSTLY_REJ) || flag (R_XHT_FIXUP); +} + + +BOOL8 REJ::rej_between_mm_and_quality_accept() { + return flag (R_BAD_QUALITY); +} + + +BOOL8 REJ::rej_between_quality_and_minimal_rej_accept() { + return flag (R_DOC_REJ) || + flag (R_BLOCK_REJ) || flag (R_ROW_REJ) || flag (R_UNLV_REJ); +} + + +BOOL8 REJ::rej_before_mm_accept() { + return rej_between_nn_and_mm () || + (rej_before_nn_accept () && + !flag (R_NN_ACCEPT) && !flag (R_HYPHEN_ACCEPT)); +} + + +BOOL8 REJ::rej_before_quality_accept() { + return rej_between_mm_and_quality_accept () || + (!flag (R_MM_ACCEPT) && rej_before_mm_accept ()); +} + + +BOOL8 REJ::rejected() { //Is char rejected? + if (flag (R_MINIMAL_REJ_ACCEPT)) + return FALSE; + else + return (perm_rejected () || + rej_between_quality_and_minimal_rej_accept () || + (!flag (R_QUALITY_ACCEPT) && rej_before_quality_accept ())); +} + + +BOOL8 REJ::accept_if_good_quality() { //potential rej? + return (rejected () && + !perm_rejected () && + flag (R_BAD_PERMUTER) && + !flag (R_POOR_MATCH) && + !flag (R_NOT_TESS_ACCEPTED) && + !flag (R_CONTAINS_BLANKS) && + (rejmap_allow_more_good_qual || + (!rej_between_nn_and_mm () && + !rej_between_mm_and_quality_accept () && + !rej_between_quality_and_minimal_rej_accept ()))); +} + + +void REJ::setrej_tess_failure() { //Tess generated blank + set_flag(R_TESS_FAILURE); +} + + +void REJ::setrej_small_xht() { //Small xht char/wd + set_flag(R_SMALL_XHT); +} + + +void REJ::setrej_edge_char() { //Close to image edge + set_flag(R_EDGE_CHAR); +} + + +void REJ::setrej_1Il_conflict() { //Initial reject map + if (rej_use_1Il_rej) + set_flag(R_1IL_CONFLICT); +} + + +void REJ::setrej_postNN_1Il() { //1Il after NN + set_flag(R_POSTNN_1IL); +} + + +void REJ::setrej_rej_cblob() { //Insert duff blob + set_flag(R_REJ_CBLOB); +} + + +void REJ::setrej_mm_reject() { //Matrix matcher + set_flag(R_MM_REJECT); +} + + +void REJ::setrej_bad_repetition() { //Odd repeated char + set_flag(R_BAD_REPETITION); +} + + +void REJ::setrej_poor_match() { //Failed Rays heuristic + set_flag(R_POOR_MATCH); +} + + +void REJ::setrej_not_tess_accepted() { + //TEMP reject_word + set_flag(R_NOT_TESS_ACCEPTED); +} + + +void REJ::setrej_contains_blanks() { + //TEMP reject_word + set_flag(R_CONTAINS_BLANKS); +} + + +void REJ::setrej_bad_permuter() { //POTENTIAL reject_word + set_flag(R_BAD_PERMUTER); +} + + +void REJ::setrej_hyphen() { //PostNN dubious hyphen or . + set_flag(R_HYPHEN); +} + + +void REJ::setrej_dubious() { //PostNN dubious limit + set_flag(R_DUBIOUS); +} + + +void REJ::setrej_no_alphanums() { //TEMP reject_word + set_flag(R_NO_ALPHANUMS); +} + + +void REJ::setrej_mostly_rej() { //TEMP reject_word + set_flag(R_MOSTLY_REJ); +} + + +void REJ::setrej_xht_fixup() { //xht fixup + set_flag(R_XHT_FIXUP); +} + + +void REJ::setrej_bad_quality() { //TEMP reject_word + set_flag(R_BAD_QUALITY); +} + + +void REJ::setrej_doc_rej() { //TEMP reject_word + set_flag(R_DOC_REJ); +} + + +void REJ::setrej_block_rej() { //TEMP reject_word + set_flag(R_BLOCK_REJ); +} + + +void REJ::setrej_row_rej() { //TEMP reject_word + set_flag(R_ROW_REJ); +} + + +void REJ::setrej_unlv_rej() { //TEMP reject_word + set_flag(R_UNLV_REJ); +} + + +void REJ::setrej_hyphen_accept() { //NN Flipped a char + set_flag(R_HYPHEN_ACCEPT); +} + + +void REJ::setrej_nn_accept() { //NN Flipped a char + set_flag(R_NN_ACCEPT); +} + + +void REJ::setrej_mm_accept() { //Matrix matcher + set_flag(R_MM_ACCEPT); +} + + +void REJ::setrej_quality_accept() { //Quality flip a char + set_flag(R_QUALITY_ACCEPT); +} + + +void REJ::setrej_minimal_rej_accept() { + //Accept all except blank + set_flag(R_MINIMAL_REJ_ACCEPT); +} + + +void REJ::full_print(FILE *fp) { + #ifndef SECURE_NAMES + + fprintf (fp, "R_TESS_FAILURE: %s\n", flag (R_TESS_FAILURE) ? "T" : "F"); + fprintf (fp, "R_SMALL_XHT: %s\n", flag (R_SMALL_XHT) ? "T" : "F"); + fprintf (fp, "R_EDGE_CHAR: %s\n", flag (R_EDGE_CHAR) ? "T" : "F"); + fprintf (fp, "R_1IL_CONFLICT: %s\n", flag (R_1IL_CONFLICT) ? "T" : "F"); + fprintf (fp, "R_POSTNN_1IL: %s\n", flag (R_POSTNN_1IL) ? "T" : "F"); + fprintf (fp, "R_REJ_CBLOB: %s\n", flag (R_REJ_CBLOB) ? "T" : "F"); + fprintf (fp, "R_MM_REJECT: %s\n", flag (R_MM_REJECT) ? "T" : "F"); + fprintf (fp, "R_BAD_REPETITION: %s\n", flag (R_BAD_REPETITION) ? "T" : "F"); + fprintf (fp, "R_POOR_MATCH: %s\n", flag (R_POOR_MATCH) ? "T" : "F"); + fprintf (fp, "R_NOT_TESS_ACCEPTED: %s\n", + flag (R_NOT_TESS_ACCEPTED) ? "T" : "F"); + fprintf (fp, "R_CONTAINS_BLANKS: %s\n", + flag (R_CONTAINS_BLANKS) ? "T" : "F"); + fprintf (fp, "R_BAD_PERMUTER: %s\n", flag (R_BAD_PERMUTER) ? "T" : "F"); + fprintf (fp, "R_HYPHEN: %s\n", flag (R_HYPHEN) ? "T" : "F"); + fprintf (fp, "R_DUBIOUS: %s\n", flag (R_DUBIOUS) ? "T" : "F"); + fprintf (fp, "R_NO_ALPHANUMS: %s\n", flag (R_NO_ALPHANUMS) ? "T" : "F"); + fprintf (fp, "R_MOSTLY_REJ: %s\n", flag (R_MOSTLY_REJ) ? "T" : "F"); + fprintf (fp, "R_XHT_FIXUP: %s\n", flag (R_XHT_FIXUP) ? "T" : "F"); + fprintf (fp, "R_BAD_QUALITY: %s\n", flag (R_BAD_QUALITY) ? "T" : "F"); + fprintf (fp, "R_DOC_REJ: %s\n", flag (R_DOC_REJ) ? "T" : "F"); + fprintf (fp, "R_BLOCK_REJ: %s\n", flag (R_BLOCK_REJ) ? "T" : "F"); + fprintf (fp, "R_ROW_REJ: %s\n", flag (R_ROW_REJ) ? "T" : "F"); + fprintf (fp, "R_UNLV_REJ: %s\n", flag (R_UNLV_REJ) ? "T" : "F"); + fprintf (fp, "R_HYPHEN_ACCEPT: %s\n", flag (R_HYPHEN_ACCEPT) ? "T" : "F"); + fprintf (fp, "R_NN_ACCEPT: %s\n", flag (R_NN_ACCEPT) ? "T" : "F"); + fprintf (fp, "R_MM_ACCEPT: %s\n", flag (R_MM_ACCEPT) ? "T" : "F"); + fprintf (fp, "R_QUALITY_ACCEPT: %s\n", flag (R_QUALITY_ACCEPT) ? "T" : "F"); + fprintf (fp, "R_MINIMAL_REJ_ACCEPT: %s\n", + flag (R_MINIMAL_REJ_ACCEPT) ? "T" : "F"); + #endif +} + + +//The REJMAP class has been hacked to use alloc_struct instead of new []. +//This is to reduce memory fragmentation only as it is rather kludgy. +//alloc_struct by-passes the call to the contsructor of REJ on each +//array element. Although the constructor is empty, the BITS16 members +//do have a constructor which sets all the flags to 0. The memset +//replaces this functionality. + +REJMAP::REJMAP( //classwise copy + const REJMAP &source) { + REJ *to; + REJ *from = source.ptr; + int i; + + len = source.length (); + + if (len > 0) { + ptr = (REJ *) alloc_struct (len * sizeof (REJ), "REJ"); + to = ptr; + for (i = 0; i < len; i++) { + *to = *from; + to++; + from++; + } + } + else + ptr = NULL; +} + + +REJMAP & REJMAP::operator= ( //assign REJMAP +const REJMAP & source //from this +) { + REJ * + to; + REJ * + from = source.ptr; + int + i; + + initialise (source.len); + to = ptr; + for (i = 0; i < len; i++) { + *to = *from; + to++; + from++; + } + return *this; +} + + +void REJMAP::initialise( //Redefine map + INT16 length) { + if (ptr != NULL) + free_struct (ptr, len * sizeof (REJ), "REJ"); + len = length; + if (len > 0) + ptr = (REJ *) memset (alloc_struct (len * sizeof (REJ), "REJ"), + 0, len * sizeof (REJ)); + else + ptr = NULL; +} + + +INT16 REJMAP::accept_count() { //How many accepted? + int i; + INT16 count = 0; + + for (i = 0; i < len; i++) { + if (ptr[i].accepted ()) + count++; + } + return count; +} + + +BOOL8 REJMAP::recoverable_rejects() { //Any non perm rejs? + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].recoverable ()) + return TRUE; + } + return FALSE; +} + + +BOOL8 REJMAP::quality_recoverable_rejects() { //Any potential rejs? + int i; + + for (i = 0; i < len; i++) { + if (ptr[i].accept_if_good_quality ()) + return TRUE; + } + return FALSE; +} + + +void REJMAP::remove_pos( //Cut out an element + INT16 pos //element to remove + ) { + REJ *new_ptr; //new, smaller map + int i; + + ASSERT_HOST (pos >= 0); + ASSERT_HOST (pos < len); + ASSERT_HOST (len > 0); + + len--; + if (len > 0) + new_ptr = (REJ *) memset (alloc_struct (len * sizeof (REJ), "REJ"), + 0, len * sizeof (REJ)); + else + new_ptr = NULL; + + for (i = 0; i < pos; i++) + new_ptr[i] = ptr[i]; //copy pre pos + + for (; pos < len; pos++) + new_ptr[pos] = ptr[pos + 1]; //copy post pos + + //delete old map + free_struct (ptr, (len + 1) * sizeof (REJ), "REJ"); + ptr = new_ptr; +} + + +void REJMAP::print(FILE *fp) { + int i; + char buff[512]; + + for (i = 0; i < len; i++) { + buff[i] = ptr[i].display_char (); + } + buff[i] = '\0'; + fprintf (fp, "\"%s\"", buff); +} + + +void REJMAP::full_print(FILE *fp) { + int i; + + for (i = 0; i < len; i++) { + ptr[i].full_print (fp); + fprintf (fp, "\n"); + } +} + + +void REJMAP::rej_word_small_xht() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + ptr[i].setrej_small_xht (); + } +} + + +void REJMAP::rej_word_tess_failure() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + ptr[i].setrej_tess_failure (); + } +} + + +void REJMAP::rej_word_not_tess_accepted() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_not_tess_accepted (); + } +} + + +void REJMAP::rej_word_contains_blanks() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_contains_blanks (); + } +} + + +void REJMAP::rej_word_bad_permuter() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_bad_permuter (); + } +} + + +void REJMAP::rej_word_xht_fixup() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_xht_fixup (); + } +} + + +void REJMAP::rej_word_no_alphanums() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_no_alphanums (); + } +} + + +void REJMAP::rej_word_mostly_rej() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_mostly_rej (); + } +} + + +void REJMAP::rej_word_bad_quality() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_bad_quality (); + } +} + + +void REJMAP::rej_word_doc_rej() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_doc_rej (); + } +} + + +void REJMAP::rej_word_block_rej() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_block_rej (); + } +} + + +void REJMAP::rej_word_row_rej() { //Reject whole word + int i; + + for (i = 0; i < len; i++) { + if (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_row_rej (); + } +} diff --git a/ccstruct/rejctmap.h b/ccstruct/rejctmap.h new file mode 100644 index 0000000000..e93a1de7a1 --- /dev/null +++ b/ccstruct/rejctmap.h @@ -0,0 +1,284 @@ +/********************************************************************** + * File: rejctmap.h (Formerly rejmap.h) + * Description: REJ and REJMAP class functions. + * Author: Phil Cheatle + * Created: Thu Jun 9 13:46:38 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + +This module may look unneccessarily verbose, but here's the philosophy... + +ALL processing of the reject map is done in this module. There are lots of +separate calls to set reject/accept flags. These have DELIBERATELY been kept +distinct so that this module can decide what to do. + +Basically, there is a flag for each sort of rejection or acceptance. This +provides a history of what has happened to EACH character. + +Determining whether a character is CURRENTLY rejected depends on implicit +understanding of the SEQUENCE of possible calls. The flags are defined and +grouped in the REJ_FLAGS enum. These groupings are used in determining a +characters CURRENT rejection status. Basically, a character is ACCEPTED if + + none of the permanent rej flags are set + AND ( the character has never been rejected + OR an accept flag is set which is LATER than the latest reject flag ) + +IT IS FUNDAMENTAL THAT ANYONE HACKING THIS CODE UNDERSTANDS THE SIGNIFICANCE +OF THIS IMPLIED TEMPORAL ORDERING OF THE FLAGS!!!! +**********************************************************************/ + +#ifndef REJCTMAP_H +#define REJCTMAP_H + +#ifdef __UNIX__ +#include +#endif +#include "memry.h" +#include "bits16.h" +#include "varable.h" +#include "notdll.h" + +extern BOOL_VAR_H (rejword_only_set_if_accepted, TRUE, +"Mimic old reject_word"); +extern BOOL_VAR_H (rejmap_allow_more_good_qual, FALSE, +"Use initial good qual setting"); +extern BOOL_VAR_H (rej_use_1Il_rej, TRUE, "1Il rejection enabled"); + +enum REJ_FLAGS +{ + /* Reject modes which are NEVER overridden */ + R_TESS_FAILURE, // PERM Tess didnt classify + R_SMALL_XHT, // PERM Xht too small + R_EDGE_CHAR, // PERM Too close to edge of image + R_1IL_CONFLICT, // PERM 1Il confusion + R_POSTNN_1IL, // PERM 1Il unrejected by NN + R_REJ_CBLOB, // PERM Odd blob + R_MM_REJECT, // PERM Matrix match rejection (m's) + R_BAD_REPETITION, // TEMP Repeated char which doesn't match trend + + /* Initial reject modes (pre NN_ACCEPT) */ + R_POOR_MATCH, // TEMP Ray's original heuristic (Not used) + R_NOT_TESS_ACCEPTED, // TEMP Tess didnt accept WERD + R_CONTAINS_BLANKS, // TEMP Tess failed on other chs in WERD + R_BAD_PERMUTER, // POTENTIAL Bad permuter for WERD + + /* Reject modes generated after NN_ACCEPT but before MM_ACCEPT */ + R_HYPHEN, // TEMP Post NN dodgy hyphen or full stop + R_DUBIOUS, // TEMP Post NN dodgy chars + R_NO_ALPHANUMS, // TEMP No alphanumerics in word after NN + R_MOSTLY_REJ, // TEMP Most of word rejected so rej the rest + R_XHT_FIXUP, // TEMP Xht tests unsure + + /* Reject modes generated after MM_ACCEPT but before QUALITY_ACCEPT */ + R_BAD_QUALITY, // TEMP Quality metrics bad for WERD + + /* Reject modes generated after QUALITY_ACCEPT but before MINIMAL_REJ accep*/ + R_DOC_REJ, // TEMP Document rejection + R_BLOCK_REJ, // TEMP Block rejection + R_ROW_REJ, // TEMP Row rejection + R_UNLV_REJ, // TEMP ~ turned to - or ^ turned to space + + /* Accept modes which occur inbetween the above rejection groups */ + R_NN_ACCEPT, //NN acceptance + R_HYPHEN_ACCEPT, //Hyphen acceptance + R_MM_ACCEPT, //Matrix match acceptance + R_QUALITY_ACCEPT, //Accept word in good quality doc + R_MINIMAL_REJ_ACCEPT //Accept EVERYTHING except tess failures +}; + +/* REJECT MAP VALUES */ + +#define MAP_ACCEPT '1' +#define MAP_REJECT_PERM '0' +#define MAP_REJECT_TEMP '2' +#define MAP_REJECT_POTENTIAL '3' + +class REJ +{ + BITS16 flags1; + BITS16 flags2; + + void set_flag(REJ_FLAGS rej_flag) { + if (rej_flag < 16) + flags1.turn_on_bit (rej_flag); + else + flags2.turn_on_bit (rej_flag - 16); + } + + BOOL8 rej_before_nn_accept(); + BOOL8 rej_between_nn_and_mm(); + BOOL8 rej_between_mm_and_quality_accept(); + BOOL8 rej_between_quality_and_minimal_rej_accept(); + BOOL8 rej_before_mm_accept(); + BOOL8 rej_before_quality_accept(); + + public: + REJ() { //constructor + } + + REJ( //classwise copy + const REJ &source) { + flags1 = source.flags1; + flags2 = source.flags2; + } + + REJ & operator= ( //assign REJ + const REJ & source) { //from this + flags1 = source.flags1; + flags2 = source.flags2; + return *this; + } + + BOOL8 flag(REJ_FLAGS rej_flag) { + if (rej_flag < 16) + return flags1.bit (rej_flag); + else + return flags2.bit (rej_flag - 16); + } + + char display_char() { + if (perm_rejected ()) + return MAP_REJECT_PERM; + else if (accept_if_good_quality ()) + return MAP_REJECT_POTENTIAL; + else if (rejected ()) + return MAP_REJECT_TEMP; + else + return MAP_ACCEPT; + } + + BOOL8 perm_rejected(); //Is char perm reject? + + BOOL8 rejected(); //Is char rejected? + + BOOL8 accepted() { //Is char accepted? + return !rejected (); + } + + //potential rej? + BOOL8 accept_if_good_quality(); + + BOOL8 recoverable() { + return (rejected () && !perm_rejected ()); + } + + void setrej_tess_failure(); //Tess generated blank + void setrej_small_xht(); //Small xht char/wd + void setrej_edge_char(); //Close to image edge + void setrej_1Il_conflict(); //Initial reject map + void setrej_postNN_1Il(); //1Il after NN + void setrej_rej_cblob(); //Insert duff blob + void setrej_mm_reject(); //Matrix matcher + //Odd repeated char + void setrej_bad_repetition(); + void setrej_poor_match(); //Failed Rays heuristic + //TEMP reject_word + void setrej_not_tess_accepted(); + //TEMP reject_word + void setrej_contains_blanks(); + void setrej_bad_permuter(); //POTENTIAL reject_word + void setrej_hyphen(); //PostNN dubious hyph or . + void setrej_dubious(); //PostNN dubious limit + void setrej_no_alphanums(); //TEMP reject_word + void setrej_mostly_rej(); //TEMP reject_word + void setrej_xht_fixup(); //xht fixup + void setrej_bad_quality(); //TEMP reject_word + void setrej_doc_rej(); //TEMP reject_word + void setrej_block_rej(); //TEMP reject_word + void setrej_row_rej(); //TEMP reject_word + void setrej_unlv_rej(); //TEMP reject_word + void setrej_nn_accept(); //NN Flipped a char + void setrej_hyphen_accept(); //Good aspect ratio + void setrej_mm_accept(); //Matrix matcher + //Quality flip a char + void setrej_quality_accept(); + //Accept all except blank + void setrej_minimal_rej_accept(); + + void full_print(FILE *fp); +}; + +class REJMAP +{ + REJ *ptr; //ptr to the chars + INT16 len; //Number of chars + + public: + REJMAP() { //constructor + ptr = NULL; + len = 0; + } + + REJMAP( //classwise copy + const REJMAP &rejmap); + + REJMAP & operator= ( //assign REJMAP + const REJMAP & source); //from this + + ~REJMAP () { //destructor + if (ptr != NULL) + free_struct (ptr, len * sizeof (REJ), "REJ"); + } + + void initialise( //Redefine map + INT16 length); + + REJ & operator[]( //access function + INT16 index) const //map index + { + ASSERT_HOST (index < len); + return ptr[index]; //no bounds checks + } + + INT32 length() const { //map length + return len; + } + + INT16 accept_count(); //How many accepted? + + INT16 reject_count() { //How many rejects? + return len - accept_count (); + } + + void remove_pos( //Cut out an element + INT16 pos); //element to remove + + void print(FILE *fp); + + void full_print(FILE *fp); + + BOOL8 recoverable_rejects(); //Any non perm rejs? + + BOOL8 quality_recoverable_rejects(); + //Any potential rejs? + + void rej_word_small_xht(); //Reject whole word + //Reject whole word + void rej_word_tess_failure(); + void rej_word_not_tess_accepted(); + //Reject whole word + //Reject whole word + void rej_word_contains_blanks(); + //Reject whole word + void rej_word_bad_permuter(); + void rej_word_xht_fixup(); //Reject whole word + //Reject whole word + void rej_word_no_alphanums(); + void rej_word_mostly_rej(); //Reject whole word + void rej_word_bad_quality(); //Reject whole word + void rej_word_doc_rej(); //Reject whole word + void rej_word_block_rej(); //Reject whole word + void rej_word_row_rej(); //Reject whole word +}; +#endif diff --git a/ccstruct/rwpoly.cpp b/ccstruct/rwpoly.cpp new file mode 100644 index 0000000000..c8d0c3e903 --- /dev/null +++ b/ccstruct/rwpoly.cpp @@ -0,0 +1,89 @@ +/********************************************************************** + * File: rwpoly.c (Formerly rw_poly.c) + * Description: latest version of manual page decomp tool + * Author: Sheelagh Lloyd + * Created: 16:05 24/3/93 + * + * This version constructs a list of blocks. + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "pageblk.h" +#include "rwpoly.h" + +#include "hpddef.h" //must be last (handpd.dll) + +#define EXTERN + +EXTERN DLLSYM PAGE_BLOCK_LIST *page_block_list; +EXTERN PAGE_BLOCK_IT page_block_it; +EXTERN BOOL_VAR (blocks_read_asc, TRUE, "Read blocks in ascii format"); +EXTERN BOOL_VAR (blocks_write_asc, TRUE, "Write blocks in ascii format"); + +DLLSYM void write_poly_blocks(FILE *blfile, PAGE_BLOCK_LIST *blocks) { + + if (blocks_write_asc) + blocks->serialise_asc (blfile); + else + blocks->serialise (blfile); + + return; +} + + +DLLSYM PAGE_BLOCK_LIST *read_poly_blocks( //read file + const char *name //file to read + ) { + FILE *infp; + int c; + INT16 number_of_pblocks; + //output list + PAGE_BLOCK_LIST *pb_list = NULL; + PAGE_BLOCK *page_block; //new block for list + INT32 len; /*length to retrive */ + PAGE_BLOCK_IT it; + + if ((infp = fopen (name, "r")) != NULL) { + if (((c = fgetc (infp)) != EOF) && (ungetc (c, infp) != EOF)) { + if (blocks_read_asc) { + pb_list = new PAGE_BLOCK_LIST; + + len = de_serialise_INT32 (infp); + it.set_to_list (pb_list); + for (; len > 0; len--) { + page_block = PAGE_BLOCK::new_de_serialise_asc (infp); + /*put on the list */ + it.add_to_end (page_block); + } + } + else + pb_list = PAGE_BLOCK_LIST::de_serialise (infp); + page_block_list = pb_list; //set global for now + } + fclose(infp); + } + else { + //can't open file + CANTOPENFILE.error ("read_poly_blocks", LOG, name); + pb_list = new PAGE_BLOCK_LIST; + page_block_list = pb_list; //set global for now + } + page_block_it.set_to_list (pb_list); + number_of_pblocks = pb_list->length (); + + printf ("%d page blocks read\n", number_of_pblocks); + return pb_list; + +} diff --git a/ccstruct/rwpoly.h b/ccstruct/rwpoly.h new file mode 100644 index 0000000000..e177bdc565 --- /dev/null +++ b/ccstruct/rwpoly.h @@ -0,0 +1,45 @@ +/********************************************************************** + * File: rwpoly.h (Formerly rw_poly.h) + * Description: latest version of manual page decomp tool + * Author: Sheelagh Lloyd + * Created: 16:05 24/3/93 + * + * This version constructs a list of blocks. + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef RWPOLY_H +#define RWPOLY_H + +#include +#include +#ifdef __MSW32__ +#include +#else +#include +#endif +#include "elst.h" +#include "pageblk.h" +#include "varable.h" + +#include "hpddef.h" //must be last (handpd.dll) + +DLLSYM void write_poly_blocks(FILE *blfile, PAGE_BLOCK_LIST *blocks); + +extern DLLSYM PAGE_BLOCK_LIST *page_block_list; +extern PAGE_BLOCK_IT page_block_it; + //read file +DLLSYM PAGE_BLOCK_LIST *read_poly_blocks(const char *name //file to read + ); +#endif diff --git a/ccstruct/statistc.cpp b/ccstruct/statistc.cpp new file mode 100644 index 0000000000..55ec9b4f0d --- /dev/null +++ b/ccstruct/statistc.cpp @@ -0,0 +1,908 @@ +/********************************************************************** + * File: statistc.c (Formerly stats.c) + * Description: Simple statistical package for integer values. + * Author: Ray Smith + * Created: Mon Feb 04 16:56:05 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include +#include +#include "memry.h" +//#include "ipeerr.h" +#include "tprintf.h" +#include "statistc.h" + +#define SEED1 0x1234 //default seeds +#define SEED2 0x5678 +#define SEED3 0x9abc + +/********************************************************************** + * STATS::STATS + * + * Construct a new stats element by allocating and zeroing the memory. + **********************************************************************/ + +STATS::STATS( //constructor + INT32 min, //min of range + INT32 max //max of range + ) { + + if (max <= min) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Illegal range for stats, Min=%d, Max=%d",min,max);*/ + min = 0; + max = 1; + } + rangemin = min; //setup + rangemax = max; + buckets = (INT32 *) alloc_mem ((max - min) * sizeof (INT32)); + if (buckets != NULL) + this->clear (); //zero it + /* else + err.log(RESULT_NO_MEMORY,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "No memory for stats, Min=%d, Max=%d",min,max); */ +} + + +STATS::STATS() { //constructor + rangemax = 0; //empty + rangemin = 0; + buckets = NULL; +} + + +/********************************************************************** + * STATS::set_range + * + * Alter the range on an existing stats element. + **********************************************************************/ + +bool STATS::set_range( //constructor + INT32 min, //min of range + INT32 max //max of range + ) { + + if (max <= min) { + return false; + } + rangemin = min; //setup + rangemax = max; + if (buckets != NULL) + free_mem(buckets); //no longer want it + buckets = (INT32 *) alloc_mem ((max - min) * sizeof (INT32)); + /* if (buckets==NULL) + return err.log(RESULT_NO_MEMORY,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "No memory for stats, Min=%d, Max=%d",min,max);*/ + + this->clear (); //zero it + return true; +} + + +/********************************************************************** + * STATS::clear + * + * Clear out the STATS class by zeroing all the buckets. + **********************************************************************/ + +void STATS::clear() { //clear out buckets + total_count = 0; + if (buckets != NULL) + memset (buckets, 0, (rangemax - rangemin) * sizeof (INT32)); + //zero it +} + + +/********************************************************************** + * STATS::~STATS + * + * Destructor for a stats class. + **********************************************************************/ + +STATS::~STATS ( //destructor +) { + if (buckets != NULL) { + free_mem(buckets); + buckets = NULL; + } +} + + +/********************************************************************** + * STATS::add + * + * Add a set of samples to (or delete from) a pile. + **********************************************************************/ + +void STATS::add( //add sample + INT32 value, //bucket + INT32 count //no to add + ) { + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return; + } + if (value <= rangemin) + buckets[0] += count; //silently clip to range + else if (value >= rangemax) + buckets[rangemax - rangemin - 1] += count; + else + //add count to cell + buckets[value - rangemin] += count; + total_count += count; //keep count of total +} + + +/********************************************************************** + * STATS::mode + * + * Find the mode of a stats class. + **********************************************************************/ + +INT32 STATS::mode() { //get mode of samples + INT32 index; //current index + INT32 max; //max cell count + INT32 maxindex; //index of max + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return rangemin; + } + for (max = 0, maxindex = 0, index = rangemax - rangemin - 1; index >= 0; + index--) { + if (buckets[index] > max) { + max = buckets[index]; //find biggest + maxindex = index; + } + } + return maxindex + rangemin; //index of biggest +} + + +/********************************************************************** + * STATS::mean + * + * Find the mean of a stats class. + **********************************************************************/ + +float STATS::mean() { //get mean of samples + INT32 index; //current index + INT32 sum; //sum of cells + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return (float) rangemin; + } + for (sum = 0, index = rangemax - rangemin - 1; index >= 0; index--) { + //sum all buckets + sum += index * buckets[index]; + } + if (total_count > 0) + //mean value + return (float) sum / total_count + rangemin; + else + return (float) rangemin; //no mean +} + + +/********************************************************************** + * STATS::sd + * + * Find the standard deviation of a stats class. + **********************************************************************/ + +float STATS::sd() { //standard deviation + INT32 index; //current index + INT32 sum; //sum of cells + INT32 sqsum; //sum of squares + float variance; + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return (float) 0.0; + } + for (sum = 0, sqsum = 0, index = rangemax - rangemin - 1; index >= 0; + index--) { + //sum all buckets + sum += index * buckets[index]; + //and squares + sqsum += index * index * buckets[index]; + } + if (total_count > 0) { + variance = sum / ((float) total_count); + variance = sqsum / ((float) total_count) - variance * variance; + return (float) sqrt (variance); + } + else + return (float) 0.0; +} + + +/********************************************************************** + * STATS::ile + * + * Find an arbitrary %ile of a stats class. + **********************************************************************/ + +float STATS::ile( //percentile + float frac //fraction to find + ) { + INT32 index; //current index + INT32 sum; //sum of cells + float target; //target value + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return (float) rangemin; + } + target = frac * total_count; + if (target <= 0) + target = (float) 1; + if (target > total_count) + target = (float) total_count; + for (sum = 0, index = 0; index < rangemax - rangemin + && sum < target; sum += buckets[index], index++); + if (index > 0) + return rangemin + index - (sum - target) / buckets[index - 1]; + //better than just ints + else + return (float) rangemin; +} + + +/********************************************************************** + * STATS::median + * + * Finds a more usefule estimate of median than ile(0.5). + * + * Overcomes a problem with ile() - if the samples are, for example, + * 6,6,13,14 ile(0.5) return 7.0 - when a more useful value would be midway + * between 6 and 13 = 9.5 + **********************************************************************/ + +float STATS::median() { //get median + float median; + INT32 min_pile; + INT32 median_pile; + INT32 max_pile; + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return (float) rangemin; + } + median = (float) ile ((float) 0.5); + median_pile = (INT32) floor (median); + if ((total_count > 1) && (pile_count (median_pile) == 0)) { + /* Find preceeding non zero pile */ + for (min_pile = median_pile; pile_count (min_pile) == 0; min_pile--); + /* Find following non zero pile */ + for (max_pile = median_pile; pile_count (max_pile) == 0; max_pile++); + median = (float) ((min_pile + max_pile) / 2.0); + } + return median; +} + + +/********************************************************************** + * STATS::smooth + * + * Apply a triangular smoothing filter to the stats. + * This makes the modes a bit more useful. + * The factor gives the height of the triangle, i.e. the weight of the + * centre. + **********************************************************************/ + +void STATS::smooth( //smooth samples + INT32 factor //size of triangle + ) { + INT32 entry; //bucket index + INT32 offset; //from entry + INT32 entrycount; //no of entries + INT32 bucket; //new smoothed pile + //output stats + STATS result(rangemin, rangemax); + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return; + } + if (factor < 2) + return; //is a no-op + entrycount = rangemax - rangemin; + for (entry = 0; entry < entrycount; entry++) { + //centre weight + bucket = buckets[entry] * factor; + for (offset = 1; offset < factor; offset++) { + if (entry - offset >= 0) + bucket += buckets[entry - offset] * (factor - offset); + if (entry + offset < entrycount) + bucket += buckets[entry + offset] * (factor - offset); + } + result.add (entry + rangemin, bucket); + } + total_count = result.total_count; + memcpy (buckets, result.buckets, entrycount * sizeof (INT32)); +} + + +/********************************************************************** + * STATS::cluster + * + * Cluster the samples into max_cluster clusters. + * Each call runs one iteration. The array of clusters must be + * max_clusters+1 in size as cluster 0 is used to indicate which samples + * have been used. + * The return value is the current number of clusters. + **********************************************************************/ + +INT32 STATS::cluster( //cluster samples + float lower, //thresholds + float upper, + float multiple, //distance threshold + INT32 max_clusters, //max no to make + STATS *clusters //array of clusters + ) { + BOOL8 new_cluster; //added one + float *centres; //cluster centres + INT32 entry; //bucket index + INT32 cluster; //cluster index + INT32 best_cluster; //one to assign to + INT32 new_centre = 0; //residual mode + INT32 new_mode; //pile count of new_centre + INT32 count; //pile to place + float dist; //from cluster + float min_dist; //from best_cluster + INT32 cluster_count; //no of clusters + + if (max_clusters < 1) + return 0; + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return 0; + } + centres = (float *) alloc_mem ((max_clusters + 1) * sizeof (float)); + if (centres == NULL) { + /* err.log(RESULT_NO_MEMORY,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "No memory for centres"); */ + return 0; + } + for (cluster_count = 1; cluster_count <= max_clusters + && clusters[cluster_count].buckets != NULL + && clusters[cluster_count].total_count > 0; cluster_count++) { + centres[cluster_count] = + (float) clusters[cluster_count].ile ((float) 0.5); + new_centre = clusters[cluster_count].mode (); + for (entry = new_centre - 1; centres[cluster_count] - entry < lower + && entry >= rangemin + && pile_count (entry) <= pile_count (entry + 1); entry--) { + count = pile_count (entry) - clusters[0].pile_count (entry); + if (count > 0) { + clusters[cluster_count].add (entry, count); + clusters[0].add (entry, count); + } + } + for (entry = new_centre + 1; entry - centres[cluster_count] < lower + && entry < rangemax + && pile_count (entry) <= pile_count (entry - 1); entry++) { + count = pile_count (entry) - clusters[0].pile_count (entry); + if (count > 0) { + clusters[cluster_count].add (entry, count); + clusters[0].add (entry, count); + } + } + } + cluster_count--; + + if (cluster_count == 0) { + clusters[0].set_range (rangemin, rangemax); + } + do { + new_cluster = FALSE; + new_mode = 0; + for (entry = 0; entry < rangemax - rangemin; entry++) { + count = buckets[entry] - clusters[0].buckets[entry]; + //remaining pile + if (count > 0) { //any to handle + min_dist = (float) MAX_INT32; + best_cluster = 0; + for (cluster = 1; cluster <= cluster_count; cluster++) { + dist = entry + rangemin - centres[cluster]; + //find distance + if (dist < 0) + dist = -dist; + if (dist < min_dist) { + min_dist = dist; //find least + best_cluster = cluster; + } + } + if (min_dist > upper //far enough for new + && (best_cluster == 0 + || entry + rangemin > centres[best_cluster] * multiple + || entry + rangemin < centres[best_cluster] / multiple)) { + if (count > new_mode) { + new_mode = count; + new_centre = entry + rangemin; + } + } + } + } + //need new and room + if (new_mode > 0 && cluster_count < max_clusters) { + cluster_count++; + new_cluster = TRUE; + if (!clusters[cluster_count].set_range (rangemin, rangemax)) + return 0; + centres[cluster_count] = (float) new_centre; + clusters[cluster_count].add (new_centre, new_mode); + clusters[0].add (new_centre, new_mode); + for (entry = new_centre - 1; centres[cluster_count] - entry < lower + && entry >= rangemin + && pile_count (entry) <= pile_count (entry + 1); entry--) { + count = pile_count (entry) - clusters[0].pile_count (entry); + if (count > 0) { + clusters[cluster_count].add (entry, count); + clusters[0].add (entry, count); + } + } + for (entry = new_centre + 1; entry - centres[cluster_count] < lower + && entry < rangemax + && pile_count (entry) <= pile_count (entry - 1); entry++) { + count = pile_count (entry) - clusters[0].pile_count (entry); + if (count > 0) { + clusters[cluster_count].add (entry, count); + clusters[0].add (entry, count); + } + } + centres[cluster_count] = + (float) clusters[cluster_count].ile ((float) 0.5); + } + } + while (new_cluster && cluster_count < max_clusters); + free_mem(centres); + return cluster_count; +} + + +/********************************************************************** + * STATS::local_min + * + * Return TRUE if this point is a local min. + **********************************************************************/ + +BOOL8 STATS::local_min( //test minness + INT32 x //of x + ) { + INT32 index; //table index + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return FALSE; + } + if (x < rangemin) + x = rangemin; + if (x >= rangemax) + x = rangemax - 1; + x -= rangemin; + if (buckets[x] == 0) + return TRUE; + for (index = x - 1; index >= 0 && buckets[index] == buckets[x]; index--); + if (index >= 0 && buckets[index] < buckets[x]) + return FALSE; + for (index = x + 1; index < rangemax - rangemin + && buckets[index] == buckets[x]; index++); + if (index < rangemax - rangemin && buckets[index] < buckets[x]) + return FALSE; + else + return TRUE; +} + + +/********************************************************************** + * STATS::print + * + * Print a summary of the stats and optionally a dump of the table. + **********************************************************************/ + +void STATS::print( //print stats table + FILE *, //Now uses tprintf instead + BOOL8 dump //dump full table + ) { + INT32 index; //table index + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return; + } + if (dump) { + for (index = 0; index < rangemax - rangemin; index++) { + tprintf ("%4d:%-3d ", rangemin + index, buckets[index]); + if (index % 8 == 7) + tprintf ("\n"); + } + tprintf ("\n"); + } + + tprintf ("Total count=%d\n", total_count); + tprintf ("Min=%d\n", (INT32) (ile ((float) 0.0))); + tprintf ("Lower quartile=%.2f\n", ile ((float) 0.25)); + tprintf ("Median=%.2f\n", ile ((float) 0.5)); + tprintf ("Upper quartile=%.2f\n", ile ((float) 0.75)); + tprintf ("Max=%d\n", (INT32) (ile ((float) 0.99999))); + tprintf ("Mean= %.2f\n", mean ()); + tprintf ("SD= %.2f\n", sd ()); +} + + +/********************************************************************** + * STATS::min_bucket + * + * Find REAL minimum bucket - ile(0.0) isnt necessarily correct + **********************************************************************/ + +INT32 STATS::min_bucket() { //Find min + INT32 min; + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return rangemin; + } + + for (min = 0; (min < rangemax - rangemin) && (buckets[min] == 0); min++); + return rangemin + min; +} + + +/********************************************************************** + * STATS::max_bucket + * + * Find REAL maximum bucket - ile(1.0) isnt necessarily correct + **********************************************************************/ + +INT32 STATS::max_bucket() { //Find max + INT32 max; + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return rangemin; + } + + for (max = rangemax - rangemin - 1; + (max > 0) && (buckets[max] == 0); max--); + return rangemin + max; +} + + +/********************************************************************** + * STATS::short_print + * + * Print a summary of the stats and optionally a dump of the table. + * ( BUT ONLY THE PART OF THE TABLE BETWEEN MIN AND MAX) + **********************************************************************/ + +void STATS::short_print( //print stats table + FILE *, //Now uses tprintf instead + BOOL8 dump //dump full table + ) { + INT32 index; //table index + INT32 min = min_bucket (); + INT32 max = max_bucket (); + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return; + } + if (dump) { + for (index = min; index <= max; index++) { + tprintf ("%4d:%-3d ", rangemin + index, buckets[index]); + if ((index - min) % 8 == 7) + tprintf ("\n"); + } + tprintf ("\n"); + } + + tprintf ("Total count=%d\n", total_count); + tprintf ("Min=%d Really=%d\n", (INT32) (ile ((float) 0.0)), min); + tprintf ("Max=%d Really=%d\n", (INT32) (ile ((float) 1.1)), max); + tprintf ("Range=%d\n", max + 1 - min); + tprintf ("Lower quartile=%.2f\n", ile ((float) 0.25)); + tprintf ("Median=%.2f\n", ile ((float) 0.5)); + tprintf ("Upper quartile=%.2f\n", ile ((float) 0.75)); + tprintf ("Mean= %.2f\n", mean ()); + tprintf ("SD= %.2f\n", sd ()); +} + + +/********************************************************************** + * STATS::plot + * + * Draw a histogram of the stats table. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void STATS::plot( //plot stats table + WINDOW window, //to draw in + float xorigin, //bottom left + float yorigin, + float xscale, //one x unit + float yscale, //one y unit + COLOUR colour //colour to draw in + ) { + INT32 index; //table index + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return; + } + interior_style (window, INT_HOLLOW, 1); + perimeter_color_index(window, colour); + + for (index = 0; index < rangemax - rangemin; index++) { + rectangle (window, xorigin + xscale * index, yorigin, + xorigin + xscale * (index + 1), + yorigin + yscale * buckets[index]); + } +} +#endif + + +/********************************************************************** + * STATS::plotline + * + * Draw a histogram of the stats table. (Line only + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void STATS::plotline( //plot stats table + WINDOW window, //to draw in + float xorigin, //bottom left + float yorigin, + float xscale, //one x unit + float yscale, //one y unit + COLOUR colour //colour to draw in + ) { + INT32 index; //table index + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return; + } + line_color_index(window, colour); + line_type(window, SOLID); + + move2d (window, xorigin, yorigin + yscale * buckets[0]); + for (index = 0; index < rangemax - rangemin; index++) { + draw2d (window, xorigin + xscale * index, + yorigin + yscale * buckets[index]); + } +} +#endif + + +/********************************************************************** + * choose_nth_item + * + * Returns the index of what would b the nth item in the array + * if the members were sorted, without actually sorting. + **********************************************************************/ + +DLLSYM INT32 choose_nth_item( //fast median + INT32 index, //index to choose + float *array, //array of items + INT32 count //no of items + ) { + static UINT16 seeds[3] = { SEED1, SEED2, SEED3 }; + //for nrand + INT32 next_sample; //next one to do + INT32 next_lesser; //space for new + INT32 prev_greater; //last one saved + INT32 equal_count; //no of equal ones + float pivot; //proposed median + float sample; //current sample + + if (count <= 1) + return 0; + if (count == 2) { + if (array[0] < array[1]) { + return index >= 1 ? 1 : 0; + } + else { + return index >= 1 ? 0 : 1; + } + } + else { + if (index < 0) + index = 0; //ensure lergal + else if (index >= count) + index = count - 1; + #ifdef __UNIX__ + equal_count = (INT32) (nrand48 (seeds) % count); + #else + equal_count = (INT32) (rand () % count); + #endif + pivot = array[equal_count]; + //fill gap + array[equal_count] = array[0]; + next_lesser = 0; + prev_greater = count; + equal_count = 1; + for (next_sample = 1; next_sample < prev_greater;) { + sample = array[next_sample]; + if (sample < pivot) { + //shuffle + array[next_lesser++] = sample; + next_sample++; + } + else if (sample > pivot) { + prev_greater--; + //juggle + array[next_sample] = array[prev_greater]; + array[prev_greater] = sample; + } + else { + equal_count++; + next_sample++; + } + } + for (next_sample = next_lesser; next_sample < prev_greater;) + array[next_sample++] = pivot; + if (index < next_lesser) + return choose_nth_item (index, array, next_lesser); + else if (index < prev_greater) + return next_lesser; //in equal bracket + else + return choose_nth_item (index - prev_greater, + array + prev_greater, + count - prev_greater) + prev_greater; + } +} + + +/********************************************************************** + * choose_nth_item + * + * Returns the index of what would b the nth item in the array + * if the members were sorted, without actually sorting. + **********************************************************************/ + +DLLSYM INT32 +choose_nth_item ( //fast median +INT32 index, //index to choose +void *array, //array of items +INT32 count, //no of items +size_t size, //element size + //comparator +int (*compar) (const void *, const void *) +) { + static UINT16 seeds[3] = { SEED1, SEED2, SEED3 }; + //for nrand + int result; //of compar + INT32 next_sample; //next one to do + INT32 next_lesser; //space for new + INT32 prev_greater; //last one saved + INT32 equal_count; //no of equal ones + INT32 pivot; //proposed median + + if (count <= 1) + return 0; + if (count == 2) { + if (compar (array, (char *) array + size) < 0) { + return index >= 1 ? 1 : 0; + } + else { + return index >= 1 ? 0 : 1; + } + } + if (index < 0) + index = 0; //ensure lergal + else if (index >= count) + index = count - 1; + #ifdef __UNIX__ + pivot = (INT32) (nrand48 (seeds) % count); + #else + pivot = (INT32) (rand () % count); + #endif + swap_entries (array, size, pivot, 0); + next_lesser = 0; + prev_greater = count; + equal_count = 1; + for (next_sample = 1; next_sample < prev_greater;) { + result = + compar ((char *) array + size * next_sample, + (char *) array + size * next_lesser); + if (result < 0) { + swap_entries (array, size, next_lesser++, next_sample++); + //shuffle + } + else if (result > 0) { + prev_greater--; + swap_entries(array, size, prev_greater, next_sample); + } + else { + equal_count++; + next_sample++; + } + } + if (index < next_lesser) + return choose_nth_item (index, array, next_lesser, size, compar); + else if (index < prev_greater) + return next_lesser; //in equal bracket + else + return choose_nth_item (index - prev_greater, + (char *) array + size * prev_greater, + count - prev_greater, size, + compar) + prev_greater; +} + + +/********************************************************************** + * swap_entries + * + * Swap 2 entries of abitrary size in-place in a table. + **********************************************************************/ + +void swap_entries( //swap in place + void *array, //array of entries + size_t size, //size of entry + INT32 index1, //entries to swap + INT32 index2) { + char tmp; + char *ptr1; //to entries + char *ptr2; + size_t count; //of bytes + + ptr1 = (char *) array + index1 * size; + ptr2 = (char *) array + index2 * size; + for (count = 0; count < size; count++) { + tmp = *ptr1; + *ptr1++ = *ptr2; + *ptr2++ = tmp; //tedious! + } +} diff --git a/ccstruct/statistc.h b/ccstruct/statistc.h new file mode 100644 index 0000000000..55484933cd --- /dev/null +++ b/ccstruct/statistc.h @@ -0,0 +1,134 @@ +/********************************************************************** + * File: statistc.h (Formerly stats.h) + * Description: Class description for STATS class. + * Author: Ray Smith + * Created: Mon Feb 04 16:19:07 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef STATISTC_H +#define STATISTC_H + +#include +#include "grphics.h" + +class DLLSYM STATS //statistics package +{ + INT32 rangemin; //min of range + INT32 rangemax; //max of range + INT32 total_count; //no of samples + INT32 *buckets; //array of cells + + public: + STATS( //constructor + INT32 min, //min of range + INT32 max); //max of range + STATS(); //empty for arrays + + ~STATS (); //destructor + + bool set_range( //change range + INT32 min, //min of range + INT32 max); //max of range + + void clear(); //empty buckets + + void add( //add sample + INT32 value, //bucket + INT32 count); //no to add + + INT32 mode(); //get mode of samples + + float mean(); //get mean of samples + + float sd(); //standard deviation + + float ile( //percentile + float frac); //[0,1] for percentil + + INT32 min_bucket(); //Find min + + INT32 max_bucket(); //Find max + + float median(); //get median of samples + + void smooth( //apply blurring + INT32 factor); //filter to stats + INT32 cluster( //cluster samples + float lower, //thresholds + float upper, + float multiple, //distance threshold + INT32 max_clusters, //max no to make + STATS *clusters); //array of clusters + + INT32 pile_count( //access function + INT32 value //pile to count + ) { + return value > rangemin ? (value < rangemax + ? buckets[value - + rangemin] : buckets[rangemax - + rangemin - + 1]) : buckets[0]; + } + + INT32 get_total() { //access function + return total_count; //total of all piles + } + + BOOL8 local_min( //test local minness + INT32 x); + + void print( //print summary/table + FILE *fp, //file to print on + BOOL8 dump); //dump whole table + + void short_print( //print summary/table + FILE *fp, //file to print on + BOOL8 dump); //dump whole table + + void plot( //draw histogram rect + WINDOW window, //window to draw in + float xorigin, //origin of histo + float yorigin, //gram + float xscale, //size of one unit + float yscale, //size of one uint + COLOUR colour); //colour to draw in + + void plotline( //draw histogram line + WINDOW window, //window to draw in + float xorigin, //origin of histo + float yorigin, //gram + float xscale, //size of one unit + float yscale, //size of one uint + COLOUR colour); //colour to draw in +}; +DLLSYM INT32 choose_nth_item( //fast median + INT32 index, //index to choose + float *array, //array of items + INT32 count //no of items + ); +DLLSYM INT32 choose_nth_item ( //fast median +INT32 index, //index to choose +void *array, //array of items +INT32 count, //no of items +size_t size, //element size + //comparator +int (*compar) (const void *, const void *) +); +void swap_entries( //swap in place + void *array, //array of entries + size_t size, //size of entry + INT32 index1, //entries to swap + INT32 index2); +#endif diff --git a/ccstruct/stepblob.cpp b/ccstruct/stepblob.cpp new file mode 100644 index 0000000000..530494f728 --- /dev/null +++ b/ccstruct/stepblob.cpp @@ -0,0 +1,277 @@ +/********************************************************************** + * File: stepblob.cpp (Formerly cblob.c) + * Description: Code for C_BLOB class. + * Author: Ray Smith + * Created: Tue Oct 08 10:41:13 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "stepblob.h" + +ELISTIZE_S (C_BLOB) +/********************************************************************** + * position_outline + * + * Position the outline in the given list at the relevant place + * according to its nesting. + **********************************************************************/ +static void position_outline( //put in place + C_OUTLINE *outline, //thing to place + C_OUTLINE_LIST *destlist //desstination list + ) { + C_OUTLINE *dest_outline; //outline from dest list + C_OUTLINE_IT it = destlist; //iterator + //iterator on children + C_OUTLINE_IT child_it = outline->child (); + + if (!it.empty ()) { + do { + dest_outline = it.data (); //get destination + //encloses dest + if (*dest_outline < *outline) { + //take off list + dest_outline = it.extract (); + //put this in place + it.add_after_then_move (outline); + //make it a child + child_it.add_to_end (dest_outline); + while (!it.at_last ()) { + it.forward (); //do rest of list + //check for other children + dest_outline = it.data (); + if (*dest_outline < *outline) { + //take off list + dest_outline = it.extract (); + child_it.add_to_end (dest_outline); + //make it a child + if (it.empty ()) + break; + } + } + return; //finished + } + //enclosed by dest + else if (*outline < *dest_outline) { + position_outline (outline, dest_outline->child ()); + //place in child list + return; //finished + } + it.forward (); + } + while (!it.at_first ()); + } + it.add_to_end (outline); //at outer level +} + + +/********************************************************************** + * plot_outline_list + * + * Draw a list of outlines in the given colour and their children + * in the child colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +static void plot_outline_list( //draw outlines + C_OUTLINE_LIST *list, //outline to draw + WINDOW window, //window to draw in + COLOUR colour, //colour to use + COLOUR child_colour //colour of children + ) { + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = list; //iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + //draw it + outline->plot (window, colour); + if (!outline->child ()->empty ()) + plot_outline_list (outline->child (), window, + child_colour, child_colour); + } +} +#endif + + +/********************************************************************** + * reverse_outline_list + * + * Reverse a list of outlines and their children. + **********************************************************************/ + +static void reverse_outline_list( //reverse outlines + C_OUTLINE_LIST *list //outline to reverse + ) { + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = list; //iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + outline->reverse (); //reverse it + if (!outline->child ()->empty ()) + reverse_outline_list (outline->child ()); + } +} + + +/********************************************************************** + * C_BLOB::C_BLOB + * + * Constructor to build a C_BLOB from a list of C_OUTLINEs. + * The C_OUTLINEs are not copied so the source list is emptied. + * The C_OUTLINEs are nested correctly in the blob. + **********************************************************************/ + +C_BLOB::C_BLOB( //constructor + C_OUTLINE_LIST *outline_list //in random order + ) { + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = outline_list;//iterator + + while (!it.empty ()) { //grab the list + outline = it.extract (); //get off the list + //put it in place + position_outline(outline, &outlines); + if (!it.empty ()) + it.forward (); + } + it.set_to_list (&outlines); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + if (outline->turn_direction () < 0) { + outline->reverse (); + reverse_outline_list (outline->child ()); + outline->set_flag (COUT_INVERSE, TRUE); + } + else { + outline->set_flag (COUT_INVERSE, FALSE); + } + } +} + + +/********************************************************************** + * C_BLOB::bounding_box + * + * Return the bounding box of the blob. + **********************************************************************/ + +BOX C_BLOB::bounding_box() { //bounding box + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = &outlines; //outlines of blob + BOX box; //bounding box + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + box += outline->bounding_box (); + } + return box; +} + + +/********************************************************************** + * C_BLOB::area + * + * Return the area of the blob. + **********************************************************************/ + +INT32 C_BLOB::area() { //area + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = &outlines; //outlines of blob + INT32 total; //total area + + total = 0; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + total += outline->area (); + } + return total; +} + + +/********************************************************************** + * C_BLOB::outer_area + * + * Return the area of the blob. + **********************************************************************/ + +INT32 C_BLOB::outer_area() { //area + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = &outlines; //outlines of blob + INT32 total; //total area + + total = 0; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + total += outline->outer_area (); + } + return total; +} + + +/********************************************************************** + * C_BLOB::count_transitions + * + * Return the total x and y maxes and mins in the blob. + * Chlid outlines are not counted. + **********************************************************************/ + +INT32 C_BLOB::count_transitions( //area + INT32 threshold //on size + ) { + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = &outlines; //outlines of blob + INT32 total; //total area + + total = 0; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + total += outline->count_transitions (threshold); + } + return total; +} + + +/********************************************************************** + * C_BLOB::move + * + * Move C_BLOB by vector + **********************************************************************/ + +void C_BLOB::move( // reposition blob + const ICOORD vec // by vector + ) { + C_OUTLINE_IT it(&outlines); // iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->move (vec); // move each outline +} + + +/********************************************************************** + * C_BLOB::plot + * + * Draw the C_BLOB in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void C_BLOB::plot( //draw it + WINDOW window, //window to draw in + COLOUR blob_colour, //main colour + COLOUR child_colour //for holes + ) { + plot_outline_list(&outlines, window, blob_colour, child_colour); +} +#endif diff --git a/ccstruct/stepblob.h b/ccstruct/stepblob.h new file mode 100644 index 0000000000..76af723d96 --- /dev/null +++ b/ccstruct/stepblob.h @@ -0,0 +1,81 @@ +/********************************************************************** + * File: stepblob.h (Formerly cblob.h) + * Description: Code for C_BLOB class. + * Author: Ray Smith + * Created: Tue Oct 08 10:41:13 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef STEPBLOB_H +#define STEPBLOB_H + +#include "coutln.h" +#include "rect.h" + +class C_BLOB:public ELIST_LINK +{ + public: + C_BLOB() { + } //empty constructor + C_BLOB( //constructor //in random order + C_OUTLINE_LIST *outline_list); + + C_OUTLINE_LIST *out_list() { //get outline list + return &outlines; + } + + BOX bounding_box(); //compute bounding box + INT32 area(); //compute area + INT32 outer_area(); //compute area + INT32 count_transitions( //count maxima + INT32 threshold); //size threshold + + void move( // reposition blob + const ICOORD vec); // by vector + + void plot( //draw one + WINDOW window, //window to draw in + COLOUR blob_colour, //for outer bits + COLOUR child_colour); //for holes + + void prep_serialise() { //set ptrs to counts + outlines.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + outlines.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + outlines.de_dump (f); + } + + //assignment + make_serialise (C_BLOB) C_BLOB & operator= ( + const C_BLOB & source) { //from this + if (!outlines.empty ()) + outlines.clear (); + + outlines.deep_copy (&source.outlines); + return *this; + } + + private: + C_OUTLINE_LIST outlines; //master elements +}; + +ELISTIZEH_S (C_BLOB) +#endif diff --git a/ccstruct/txtregn.cpp b/ccstruct/txtregn.cpp new file mode 100644 index 0000000000..37dd776f31 --- /dev/null +++ b/ccstruct/txtregn.cpp @@ -0,0 +1,230 @@ +/********************************************************************** + * File: txtregn.c (Formerly text_region.c) + * Description: Text region within a polygonal block + * Author: Sheelagh Lloyd? + * Created: + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include +#include "txtregn.h" +#include "labls.h" + +#include "hpddef.h" //must be last (handpd.dll) + +ELISTIZE_S (TEXT_REGION) TEXT_REGION::TEXT_REGION (INT32 idno, ICOORDELT_LIST * points, TEXT_REGION_LIST * child):POLY_BLOCK (points, +POLY_TEXT) { + TEXT_REGION_IT + c = &txt_regions; + + id_number = idno; + txt_regions.clear (); + c.move_to_first (); + c.add_list_before (child); +} + + +TEXT_REGION::TEXT_REGION (INT32 idno, ICOORDELT_LIST * points):POLY_BLOCK (points, +POLY_TEXT) { + id_number = idno; + + txt_regions.clear (); +} + + +TEXT_REGION::TEXT_REGION ( //constructor +INT32 idno, ICOORDELT_LIST * points, INT8 hor, INT8 tex, INT8 ser, INT8 pro, INT8 nor, INT8 upr, INT8 sol, INT8 bla, INT8 und, INT8 dro):POLY_BLOCK (points, +POLY_TEXT) { + + id_number = idno; + horizontal = hor; + text = tex; + serif = ser; + proportional = pro; + normal = nor; + upright = upr; + solid = sol; + black = bla; + underlined = und; + dropcaps = dro; + + txt_regions.clear (); +} + + +void TEXT_REGION::set_attrs(INT8 hor, + INT8 tex, + INT8 ser, + INT8 pro, + INT8 nor, + INT8 upr, + INT8 sol, + INT8 bla, + INT8 und, + INT8 dro) { + + horizontal = hor; + text = tex; + serif = ser; + proportional = pro; + normal = nor; + upright = upr; + solid = sol; + black = bla; + underlined = und; + dropcaps = dro; +} + + +#include "hpddef.h" //must be last (handpd.dll) +void TEXT_REGION::show_attrs( //Now uses tprintf instead + DEBUG_WIN *f) { + TEXT_REGION_IT it = &txt_regions; + + if (id_number > -1) { + f-> + dprintf + ("Text region no. %d with attributes %s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n", + id_number, tlabel[0][horizontal], tlabel[1][text], + tlabel[2][serif], tlabel[3][proportional], tlabel[4][normal], + tlabel[5][upright], tlabel[6][solid], tlabel[7][black], + tlabel[8][underlined], tlabel[9][dropcaps]); + if (!txt_regions.empty ()) { + f->dprintf ("with text subregions\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->show_attrs (f); + f->dprintf ("end of subregions\n"); + } + } + else + f-> + dprintf + ("Text subregion with attributes %s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n", + tlabel[0][horizontal], tlabel[1][text], tlabel[2][serif], + tlabel[3][proportional], tlabel[4][normal], tlabel[5][upright], + tlabel[6][solid], tlabel[7][black], tlabel[8][underlined], + tlabel[9][dropcaps]); +} + + +void TEXT_REGION::add_a_region(TEXT_REGION *newchild) { + TEXT_REGION_IT c = &txt_regions; + + c.move_to_first (); + c.add_to_end (newchild); +} + + +/********************************************************************** + * TEXT_REGION::rotate + * + * Rotate the TEXT_REGION and its children + **********************************************************************/ + +void TEXT_REGION::rotate( //cos,sin + FCOORD rotation) { + //sub block iterator + TEXT_REGION_IT child_it = &txt_regions; + TEXT_REGION *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->rotate (rotation); + } + POLY_BLOCK::rotate(rotation); +} + + +/********************************************************************** + * TEXT_REGION::move + * + * Move the TEXT_REGION and its children + **********************************************************************/ + +void TEXT_REGION::move(ICOORD shift //amount to move + ) { + //sub block iterator + TEXT_REGION_IT child_it = &txt_regions; + TEXT_REGION *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->move (shift); + } + POLY_BLOCK::move(shift); +} + + +/********************************************************************** + * TEXT_REGION::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void TEXT_REGION::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((POLY_BLOCK *) this)->serialise_asc (f); + serialise_INT32(f, id_number); //unique id + //horizontal, vertical, skewed + serialise_INT32(f, horizontal); + serialise_INT32(f, text); //text, table, form + serialise_INT32(f, serif); //serif, sansserif + //proportional, fixed + serialise_INT32(f, proportional); + serialise_INT32(f, normal); //normal, bold + serialise_INT32(f, upright); //upright, italic + serialise_INT32(f, solid); //solid, outline + serialise_INT32(f, black); //black, coloured, white, + //not underlined, underlined + serialise_INT32(f, underlined); + serialise_INT32(f, dropcaps); //not dropcaps, dropcaps + + txt_regions.serialise_asc (f); +} + + +/********************************************************************** + * TEXT_REGION::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void TEXT_REGION::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((POLY_BLOCK *) this)->de_serialise_asc (f); + //unique id + id_number = de_serialise_INT32 (f); + //horizontal, vertical, skewed + horizontal = de_serialise_INT32 (f); + text = de_serialise_INT32 (f); //text, table, form + serif = de_serialise_INT32 (f);//serif, sansserif + //proportional, fixed + proportional = de_serialise_INT32 (f); + //normal, bold + normal = de_serialise_INT32 (f); + //upright, italic + upright = de_serialise_INT32 (f); + solid = de_serialise_INT32 (f);//solid, outline + black = de_serialise_INT32 (f);//black, coloured, white, + //not underlined, underlined + underlined = de_serialise_INT32 (f); + //not dropcaps, dropcaps + dropcaps = de_serialise_INT32 (f); + + txt_regions.de_serialise_asc (f); +} diff --git a/ccstruct/txtregn.h b/ccstruct/txtregn.h new file mode 100644 index 0000000000..671d6012af --- /dev/null +++ b/ccstruct/txtregn.h @@ -0,0 +1,155 @@ +/********************************************************************** + * File: txtregn.h (Formerly text_region.h) + * Description: Text region within a polygonal block + * Author: Sheelagh Lloyd? + * Created: + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TXTREGN_H +#define TXTREGN_H + +#include +#include "elst.h" +#include "hpdsizes.h" +#include "polyblk.h" +#include "debugwin.h" + +#include "hpddef.h" //must be last (handpd.dll) + +#define REGION_COLOUR CYAN +#define SUBREGION_COLOUR GREEN + +class DLLSYM TEXT_REGION; //forward decl + +ELISTIZEH_S (TEXT_REGION) +class DLLSYM TEXT_REGION:public ELIST_LINK, public POLY_BLOCK +//text REGION +{ + public: + TEXT_REGION() { + } //empty constructor + TEXT_REGION( //simple constructor + INT32 idno, + ICOORDELT_LIST *points, + TEXT_REGION_LIST *child); + + TEXT_REGION( //simple constructor + INT32 idno, + ICOORDELT_LIST *points); + + TEXT_REGION( //constructor + INT32 idno, + ICOORDELT_LIST *points, + INT8 hor, + INT8 tex, + INT8 ser, + INT8 pro, + INT8 nor, + INT8 upr, + INT8 sol, + INT8 bla, + INT8 und, + INT8 dro); + + ~TEXT_REGION () { //destructor + } + + void set_id_no(INT32 new_id) { + id_number = new_id; + } + + INT32 id_no() { + return id_number; + } + + INT32 nregions() { + return txt_regions.length (); + } + + BOOL8 is_prop() const { //test proportional + return !proportional; //stored negatively + } + + void set_prop(BOOL8 prop) { + if (prop) + proportional = 0; + else + proportional = 1; + } + + void add_a_region(TEXT_REGION *newchild); + + //get children + TEXT_REGION_LIST *regions() { + return &txt_regions; + } + + void set_attrs(INT8 hor, + INT8 tex, + INT8 ser, + INT8 pro, + INT8 nor, + INT8 upr, + INT8 sol, + INT8 bla, + INT8 und, + INT8 dro); + + void show_attrs(DEBUG_WIN *f); + + void rotate( //rotate it + FCOORD rotation); + void move( //move it + ICOORD shift); //vector + + void prep_serialise() { //set ptrs to counts + POLY_BLOCK::prep_serialise(); + txt_regions.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + POLY_BLOCK::dump(f); + txt_regions.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + POLY_BLOCK::de_dump(f); + txt_regions.de_dump (f); + } + + //serialise to ascii + make_serialise (TEXT_REGION) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + INT32 id_number; //unique id + INT8 horizontal; //horizontal, vertical, skewed + INT8 text; //text, table, form + INT8 serif; //serif, sansserif + INT8 proportional; //proportional, fixed + INT8 normal; //normal, bold + INT8 upright; //upright, italic + INT8 solid; //solid, outline + INT8 black; //black, coloured, white, + INT8 underlined; //not underlined, underlined + INT8 dropcaps; //not dropcaps, dropcaps + + TEXT_REGION_LIST txt_regions; +}; +#endif diff --git a/ccstruct/vecfuncs.cpp b/ccstruct/vecfuncs.cpp new file mode 100644 index 0000000000..33992e1c28 --- /dev/null +++ b/ccstruct/vecfuncs.cpp @@ -0,0 +1,63 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: vecfuncs.c (Formerly vecfuncs.c) + * Description: Blob definition + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 27 15:39:52 1989 + * Modified: Tue Jul 9 17:44:12 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + * Revision 5.1 89/07/27 11:47:50 11:47:50 ray () + * Added ratings acces methods. + * This version ready for independent development. + */ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "mfcpch.h" +#include "vecfuncs.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * direction + * + * Show if the line is going in the positive or negative X direction. + **********************************************************************/ +int direction(EDGEPT *point) { + int dir; /** direction to return **/ + EDGEPT *prev; /** prev point **/ + EDGEPT *next; /** next point **/ + + dir = 0; + prev = point->prev; + next = point->next; + + if (((prev->pos.x <= point->pos.x) && + (point->pos.x < next->pos.x)) || + ((prev->pos.x < point->pos.x) && (point->pos.x <= next->pos.x))) + dir = 1; + + if (((prev->pos.x >= point->pos.x) && + (point->pos.x > next->pos.x)) || + ((prev->pos.x > point->pos.x) && (point->pos.x >= next->pos.x))) + dir = -1; + + return dir; +} diff --git a/ccstruct/vecfuncs.h b/ccstruct/vecfuncs.h new file mode 100644 index 0000000000..844d036f21 --- /dev/null +++ b/ccstruct/vecfuncs.h @@ -0,0 +1,91 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: vecfuncs.h (Formerly vecfuncs.h) + * Description: Vector calculations + * Author: Mark Seaman, OCR Technology + * Created: Wed Dec 20 09:37:18 1989 + * Modified: Tue Jul 9 17:44:37 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef VECFUNCS_H +#define VECFUNCS_H + +#include "tessclas.h" +#include + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * point_diff + * + * Return the difference from point (p1) to point (p2). Put the value + * into point (p). + **********************************************************************/ + +#define point_diff(p,p1,p2) \ +((p).x = (p1).x - (p2).x, \ + (p).y = (p1).y - (p2).y, \ + (p)) + +/********************************************************************** + * CROSS + * + * cross product + **********************************************************************/ + +#define CROSS(a,b) \ +((a).x * (b).y - (a).y * (b).x) + +/********************************************************************** + * SCALAR + * + * scalar vector product + **********************************************************************/ + +#define SCALAR(a,b) \ +((a).x * (b).x + (a).y * (b).y) + +/********************************************************************** + * LENGTH + * + * length of vector + **********************************************************************/ + +#define LENGTH(a) \ +((a).x * (a).x + (a).y * (a).y) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +int direction(EDGEPT *point); + +/* +#if defined(__STDC__) || defined(__cplusplus) || MAC_OR_DOS +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* vecfuncs.c +int direction + _ARGS((EDGEPT *point)); + +#undef _ARGS +*/ +#endif diff --git a/ccstruct/werd.cpp b/ccstruct/werd.cpp new file mode 100644 index 0000000000..b4bff9dd8a --- /dev/null +++ b/ccstruct/werd.cpp @@ -0,0 +1,997 @@ +/********************************************************************** + * File: werd.cpp (Formerly word.c) + * Description: Code for the WERD class. + * Author: Ray Smith + * Created: Tue Oct 08 14:32:12 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "blckerr.h" +#include "linlsq.h" +#include "werd.h" + +#define FIRST_COLOUR RED //first rainbow colour + //last rainbow colour +#define LAST_COLOUR AQUAMARINE +#define CHILD_COLOUR BROWN //colour of children + +const ERRCODE CANT_SCALE_EDGESTEPS = +"Attempted to scale an edgestep format word"; + +#define EXTERN + +EXTERN BOOL_VAR (bln_numericmode, 0, "Optimize for numbers"); +EXTERN INT_VAR (bln_x_height, 128, "Baseline Normalisation X-height"); +EXTERN INT_VAR (bln_baseline_offset, 64, "Baseline Norm. offset of baseline"); +EXTERN double_VAR (bln_blshift_maxshift, -1.0, +"Fraction of xh before shifting"); +EXTERN double_VAR (bln_blshift_xfraction, 0.75, +"Size fraction of xh before shifting"); + +ELISTIZE_S (WERD) +/********************************************************************** + * WERD::WERD + * + * Constructor to build a WERD from a list of C_BLOBs. + * The C_BLOBs are not copied so the source list is emptied. + **********************************************************************/ +WERD::WERD ( //constructor +C_BLOB_LIST * blob_list, //in word order +UINT8 blank_count, //blanks in front +const char *text //correct text +): +flags (0), +correct(text) { + C_BLOB_IT start_it = blob_list;//iterator + C_BLOB_IT end_it = blob_list; //another + //rejected blobs in wd + C_BLOB_IT rej_cblob_it = &rej_cblobs; + C_OUTLINE_IT c_outline_it; //coutline iterator + BOOL8 blob_inverted; + BOOL8 reject_blob; + INT16 inverted_vote = 0; + INT16 non_inverted_vote = 0; + + while (!end_it.at_last ()) + end_it.forward (); //move to last + //move to our list + cblobs.assign_to_sublist (&start_it, &end_it); + blanks = blank_count; + /* + Set white on black flag for the WERD, moving any duff blobs onto the + rej_cblobs list. + First, walk the cblobs checking the inverse flag for each outline of each + cblob. If a cblob has inconsistent flag settings for its different + outlines, move the blob to the reject list. Otherwise, increment the + appropriate w-on-b or b-on-w vote for the word. + + Now set the inversion flag for the WERD by maximum vote. + + Walk the blobs again, moving any blob whose inversion flag does not agree + with the concencus onto the reject list. + */ + start_it.set_to_list (&cblobs); + if (start_it.empty ()) + return; + for (start_it.mark_cycle_pt (); + !start_it.cycled_list (); start_it.forward ()) { + c_outline_it.set_to_list (start_it.data ()->out_list ()); + blob_inverted = c_outline_it.data ()->flag (COUT_INVERSE); + reject_blob = FALSE; + for (c_outline_it.mark_cycle_pt (); + !c_outline_it.cycled_list () && !reject_blob; + c_outline_it.forward ()) { + reject_blob = + c_outline_it.data ()->flag (COUT_INVERSE) != blob_inverted; + } + if (reject_blob) + rej_cblob_it.add_after_then_move (start_it.extract ()); + else { + if (blob_inverted) + inverted_vote++; + else + non_inverted_vote++; + } + } + + flags.set_bit (W_INVERSE, (inverted_vote > non_inverted_vote)); + + start_it.set_to_list (&cblobs); + if (start_it.empty ()) + return; + for (start_it.mark_cycle_pt (); + !start_it.cycled_list (); start_it.forward ()) { + c_outline_it.set_to_list (start_it.data ()->out_list ()); + if (c_outline_it.data ()->flag (COUT_INVERSE) != flags.bit (W_INVERSE)) + rej_cblob_it.add_after_then_move (start_it.extract ()); + } +} + + +/********************************************************************** + * WERD::WERD + * + * Constructor to build a WERD from a list of BLOBs. + * The BLOBs are not copied so the source list is emptied. + **********************************************************************/ + +WERD::WERD ( //constructor +PBLOB_LIST * blob_list, //in word order +UINT8 blank_count, //blanks in front +const char *text //correct text +): +flags (0), +correct(text) { + PBLOB_IT start_it = blob_list; //iterator + PBLOB_IT end_it = blob_list; //another + + while (!end_it.at_last ()) + end_it.forward (); //move to last + ((PBLOB_LIST *) (&cblobs))->assign_to_sublist (&start_it, &end_it); + //move to our list + //it's a polygon + flags.set_bit (W_POLYGON, TRUE); + blanks = blank_count; + // fprintf(stderr,"Wrong constructor!!!!\n"); +} + + +/********************************************************************** + * WERD::WERD + * + * Constructor to build a WERD from a list of BLOBs. + * The BLOBs are not copied so the source list is emptied. + **********************************************************************/ + +WERD::WERD ( //constructor +PBLOB_LIST * blob_list, //in word order +WERD * clone //sorce of flags +):flags (clone->flags), correct (clone->correct) { + PBLOB_IT start_it = blob_list; //iterator + PBLOB_IT end_it = blob_list; //another + + while (!end_it.at_last ()) + end_it.forward (); //move to last + ((PBLOB_LIST *) (&cblobs))->assign_to_sublist (&start_it, &end_it); + //move to our list + blanks = clone->blanks; + // fprintf(stderr,"Wrong constructor!!!!\n"); +} + + +/********************************************************************** + * WERD::WERD + * + * Constructor to build a WERD from a list of C_BLOBs. + * The C_BLOBs are not copied so the source list is emptied. + **********************************************************************/ + +WERD::WERD ( //constructor +C_BLOB_LIST * blob_list, //in word order +WERD * clone //sorce of flags +):flags (clone->flags), correct (clone->correct) { + C_BLOB_IT start_it = blob_list;//iterator + C_BLOB_IT end_it = blob_list; //another + + while (!end_it.at_last ()) + end_it.forward (); //move to last + ((C_BLOB_LIST *) (&cblobs))->assign_to_sublist (&start_it, &end_it); + //move to our list + blanks = clone->blanks; + // fprintf(stderr,"Wrong constructor!!!!\n"); +} + + +/********************************************************************** + * WERD::poly_copy + * + * Make a copy of a WERD in polygon format. + * The source WERD is untouched. + **********************************************************************/ + +WERD *WERD::poly_copy( //make a poly copy + float xheight //row height + ) { + PBLOB *blob; //new blob + WERD *result = new WERD; //output word + C_BLOB_IT src_it = &cblobs; //iterator + // LARC_BLOB_IT larc_it=(LARC_BLOB_LIST*)(&cblobs); + PBLOB_IT dest_it = (PBLOB_LIST *) (&result->cblobs); + //another + + if (flags.bit (W_POLYGON)) { + *result = *this; //just copy it + } + else { + result->flags = flags; + result->correct = correct; //copy info + result->dummy = dummy; + if (!src_it.empty ()) { + // if (flags.bit(W_LINEARC)) + // { + // do + // { + // blob=new PBLOB; + // poly_linearc_outlines(larc_it.data()->out_list(), + // blob->out_list()); //convert outlines + // dest_it.add_after_then_move(blob); //add to dest list + // larc_it.forward(); + // } + // while (!larc_it.at_first()); + // } + // else + // { + do { + blob = new PBLOB (src_it.data (), xheight); + //convert blob + //add to dest list + dest_it.add_after_then_move (blob); + src_it.forward (); + } + while (!src_it.at_first ()); + // } + } + if (!rej_cblobs.empty ()) { + /* Polygonal approx of reject blobs */ + src_it.set_to_list (&rej_cblobs); + dest_it = (PBLOB_LIST *) (&result->rej_cblobs); + do { + //convert blob + blob = new PBLOB (src_it.data (), xheight); + //add to dest list + dest_it.add_after_then_move (blob); + src_it.forward (); + } + while (!src_it.at_first ()); + } + //polygon now + result->flags.set_bit (W_POLYGON, TRUE); + result->blanks = blanks; + } + return result; +} + + +/********************************************************************** + * WERD::bounding_box + * + * Return the bounding box of the WERD. + * This is quite a mess to compute! + * ORIGINALLY, REJECT CBLOBS WERE EXCLUDED, however, this led to bugs when the + * words on the row were re-sorted. The original words were built with reject + * blobs included. The FUZZY SPACE flags were set accordingly. If ALL the + * blobs in a word are rejected the BB for the word is NULL, causing the sort + * to screw up, leading to the erroneous possibility of the first word in a + * row being marked as FUZZY space. + **********************************************************************/ + +BOX WERD::bounding_box() { //bounding box + BOX box; //box being built + //rejected blobs in wd + C_BLOB_IT rej_cblob_it = &rej_cblobs; + + for (rej_cblob_it.mark_cycle_pt (); + !rej_cblob_it.cycled_list (); rej_cblob_it.forward ()) { + box += rej_cblob_it.data ()->bounding_box (); + } + + if (flags.bit (W_POLYGON)) { + //polygons + PBLOB_IT it = (PBLOB_LIST *) (&cblobs); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + box += it.data ()->bounding_box (); + } + } + else { + C_BLOB_IT it = &cblobs; //blobs of WERD + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + box += it.data ()->bounding_box (); + } + } + return box; +} + + +/********************************************************************** + * WERD::move + * + * Reposition WERD by vector + * NOTE!! REJECT CBLOBS ARE NOT MOVED + **********************************************************************/ + +void WERD::move( // reposition WERD + const ICOORD vec // by vector + ) { + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + // LARC_BLOB_IT lblob_it((LARC_BLOB_LIST*)&cblobs); + C_BLOB_IT cblob_it(&cblobs); // cblob iterator + + if (flags.bit (W_POLYGON)) + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) + blob_it.data ()->move (vec); + // else if (flags.bit(W_LINEARC)) + // for( lblob_it.mark_cycle_pt(); + // !lblob_it.cycled_list(); + // lblob_it.forward() ) + // lblob_it.data()->move( vec ); + else + for (cblob_it.mark_cycle_pt (); + !cblob_it.cycled_list (); cblob_it.forward ()) + cblob_it.data ()->move (vec); +} + + +/********************************************************************** + * WERD::scale + * + * Scale WERD by multiplier + **********************************************************************/ + +void WERD::scale( // scale WERD + const float f // by multiplier + ) { + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + // LARC_BLOB_IT lblob_it((LARC_BLOB_LIST*)&cblobs); + + if (flags.bit (W_POLYGON)) + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) + blob_it.data ()->scale (f); + // else if (flags.bit(W_LINEARC)) + // for (lblob_it.mark_cycle_pt(); + // !lblob_it.cycled_list(); + // lblob_it.forward() ) + // lblob_it.data()->scale( f ); + else + CANT_SCALE_EDGESTEPS.error ("WERD::scale", ABORT, NULL); +} + + +/********************************************************************** + * WERD::join_on + * + * Join other word onto this one. Delete the old word. + **********************************************************************/ + +void WERD::join_on( // join WERD + WERD *&other //other word + ) { + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + PBLOB_IT src_it ((PBLOB_LIST *) & other->cblobs); + C_BLOB_IT rej_cblob_it(&rej_cblobs); + C_BLOB_IT src_rej_it (&other->rej_cblobs); + + while (!src_it.empty ()) { + blob_it.add_to_end (src_it.extract ()); + src_it.forward (); + } + while (!src_rej_it.empty ()) { + rej_cblob_it.add_to_end (src_rej_it.extract ()); + src_rej_it.forward (); + } +} + + +/********************************************************************** + * WERD::copy_on + * + * Copy blobs from other word onto this one. + **********************************************************************/ + +void WERD::copy_on( //copy blobs + WERD *&other //from other + ) { + if (flags.bit (W_POLYGON)) { + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + PBLOB_LIST blobs; + + blobs.deep_copy ((PBLOB_LIST *) (&other->cblobs)); + blob_it.move_to_last (); + blob_it.add_list_after (&blobs); + } + // else if (flags.bit(W_LINEARC)) + // { + // LARC_BLOB_IT larc_blob_it( (LARC_BLOB_LIST*)&cblobs ); + // LARC_BLOB_LIST larc_blobs; + + // larc_blobs.deep_copy((LARC_BLOB_LIST*)(&other->cblobs)); + // larc_blob_it.move_to_last(); + // larc_blob_it.add_list_after( &larc_blobs ); + // } + else { + C_BLOB_IT c_blob_it(&cblobs); + C_BLOB_LIST c_blobs; + + c_blobs.deep_copy (&other->cblobs); + c_blob_it.move_to_last (); + c_blob_it.add_list_after (&c_blobs); + } + if (!other->rej_cblobs.empty ()) { + C_BLOB_IT rej_c_blob_it(&rej_cblobs); + C_BLOB_LIST new_rej_c_blobs; + + new_rej_c_blobs.deep_copy (&other->rej_cblobs); + rej_c_blob_it.move_to_last (); + rej_c_blob_it.add_list_after (&new_rej_c_blobs); + } +} + + +/********************************************************************** + * WERD::baseline_normalise + * + * Baseline Normalise the word in Tesseract style. (I.e origin at centre of + * word at bottom. x-height region scaled to region y = + * (bln_baseline_offset)..(bln_baseline_offset + bln_x_height) + * - usually 64..192) + **********************************************************************/ + +void WERD::baseline_normalise( // Tess style BL Norm + ROW *row, + DENORM *denorm //antidote + ) { + baseline_normalise_x (row, row->x_height (), denorm); + //Use standard x ht +} + + +/********************************************************************** + * WERD::baseline_normalise_x + * + * Baseline Normalise the word in Tesseract style. (I.e origin at centre of + * word at bottom. x-height region scaled to region y = + * (bln_baseline_offset)..(bln_baseline_offset + bln_x_height) + * - usually 64..192) + * USE A SPECIFIED X-HEIGHT - NOT NECESSARILY THE ONE IN row + **********************************************************************/ + +void WERD::baseline_normalise_x( // Tess style BL Norm + ROW *row, + float x_height, //non standard value + DENORM *denorm //antidote + ) { + BOOL8 using_row; //as baseline + float blob_x_centre; //middle of blob + float blob_offset; //bottom miss + float top_offset; //top miss + float blob_x_height; //xh for this blob + INT16 segments; //no of segments + INT16 segment; //current segment + DENORM_SEG *segs; //array of segments + float mean_x; //mean xheight + INT32 x_count; //no of xs + BOX word_box = bounding_box ();//word bounding box + BOX blob_box; //blob bounding box + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + PBLOB *blob; + LLSQ line; //fitted line + double line_m, line_c; //fitted line + //inverse norm + DENORM antidote (word_box.left () + + + (word_box.right () - word_box.left ()) / 2.0, + bln_x_height / x_height, row); + + if (!flags.bit (W_POLYGON)) { + WRONG_WORD.error ("WERD::baseline_normalise", ABORT, + "Need to poly approx"); + } + + if (flags.bit (W_NORMALIZED)) { + WRONG_WORD.error ("WERD::baseline_normalise", ABORT, + "Baseline unnormalised"); + } + + if (bln_numericmode) { + segs = new DENORM_SEG[blob_it.length ()]; + segments = 0; + float factor; // For scaling to baseline normalised size. + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob->move (FCOORD (-antidote.origin (), + -blob_box.bottom ())); + factor = bln_x_height * 4.0f / (3 * blob_box.height ()); + // Constrain the scale factor as target numbers should be either + // cap height already or xheight. + if (factor < antidote.scale()) + factor = antidote.scale(); + else if (factor > antidote.scale() * 1.5f) + factor = antidote.scale() * 1.5f; + blob->scale (factor); + blob->move (FCOORD (0.0, bln_baseline_offset)); + segs[segments].xstart = blob->bounding_box().left(); + segs[segments].ycoord = blob_box.bottom(); + segs[segments++].scale_factor = factor; + } + antidote = DENORM (antidote.origin (), antidote.scale (), + 0.0f, 0.0f, segments, segs, true, row); + delete [] segs; + + //Repeat for rej blobs + blob_it.set_to_list ((PBLOB_LIST *) & rej_cblobs); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob->move (FCOORD (-antidote.origin (), + -blob_box.bottom ())); + blob->scale (bln_x_height * 4.0f / (3 * blob_box.height ())); + blob->move (FCOORD (0.0, bln_baseline_offset)); + } + } + else if (bln_blshift_maxshift < 0) { + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + blob->move (FCOORD (-antidote.origin (), + -(row->base_line (blob_x_centre)))); + blob->scale (antidote.scale ()); + blob->move (FCOORD (0.0, bln_baseline_offset)); + } + + //Repeat for rej blobs + blob_it.set_to_list ((PBLOB_LIST *) & rej_cblobs); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + blob->move (FCOORD (-antidote.origin (), + -(row->base_line (blob_x_centre)))); + blob->scale (antidote.scale ()); + blob->move (FCOORD (0.0, bln_baseline_offset)); + } + + } + else { + mean_x = x_height; + x_count = 1; + segs = new DENORM_SEG[blob_it.length ()]; + segments = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + if (blob_box.height () > bln_blshift_xfraction * x_height) { + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + blob_offset = + blob_box.bottom () - row->base_line (blob_x_centre); + top_offset = blob_offset + blob_box.height () - x_height - 1; + blob_x_height = top_offset + x_height; + if (top_offset < 0) + top_offset = -top_offset; + if (blob_offset < 0) + blob_offset = -blob_offset; + if (blob_offset < bln_blshift_maxshift * x_height) { + segs[segments].ycoord = blob_box.bottom (); + line.add (blob_x_centre, blob_box.bottom ()); + if (top_offset < bln_blshift_maxshift * x_height) { + segs[segments].scale_factor = blob_box.height () - 1.0f; + x_count++; + } + else + segs[segments].scale_factor = 0.0f; + //fix it later + } + else { + //not a goer + segs[segments].ycoord = -MAX_INT32; + if (top_offset < bln_blshift_maxshift * x_height) { + segs[segments].scale_factor = blob_x_height; + x_count++; + } + else + segs[segments].scale_factor = 0.0f; + //fix it later + } + } + else { + segs[segments].scale_factor = 0.0f; + segs[segments].ycoord = -MAX_INT32; + } + segs[segments].xstart = blob_box.left (); + segments++; + } + using_row = line.count () <= 1; + if (!using_row) { + line_m = line.m (); + line_c = line.c (line_m); + } + else + line_m = line_c = 0; + segments = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + if (segs[segments].ycoord == -MAX_INT32 + && segs[segments].scale_factor != 0 && !using_row) { + blob_offset = line_m * blob_x_centre + line_c; + segs[segments].scale_factor = blob_box.top () - blob_offset; + } + if (segs[segments].scale_factor != 0) + mean_x += segs[segments].scale_factor; + segments++; + } + mean_x /= x_count; + // printf("mean x=%g, count=%d, line_m=%g, line_c=%g\n", + // mean_x,x_count,line_m,line_c); + segments = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + if (segs[segments].ycoord != -MAX_INT32) + blob_offset = (float) segs[segments].ycoord; + else if (using_row) + blob_offset = row->base_line (blob_x_centre); + else + blob_offset = line_m * blob_x_centre + line_c; + if (segs[segments].scale_factor == 0) + segs[segments].scale_factor = mean_x; + segs[segments].scale_factor = + bln_x_height / segs[segments].scale_factor; + // printf("Blob sf=%g, top=%d, bot=%d, base=%g\n", + // segs[segments].scale_factor,blob_box.top(), + // blob_box.bottom(),blob_offset); + blob->move (FCOORD (-antidote.origin (), -blob_offset)); + blob-> + scale (FCOORD (antidote.scale (), segs[segments].scale_factor)); + blob->move (FCOORD (0.0, bln_baseline_offset)); + segments++; + } + + //Repeat for rej blobs + blob_it.set_to_list ((PBLOB_LIST *) & rej_cblobs); + segment = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + while (segment < segments - 1 + && segs[segment + 1].xstart <= blob_x_centre) + segment++; + if (segs[segment].ycoord != -MAX_INT32) + blob_offset = (float) segs[segment].ycoord; + else if (using_row) + blob_offset = row->base_line (blob_x_centre); + else + blob_offset = line_m * blob_x_centre + line_c; + blob->move (FCOORD (-antidote.origin (), -blob_offset)); + blob-> + scale (FCOORD (antidote.scale (), segs[segment].scale_factor)); + blob->move (FCOORD (0.0, bln_baseline_offset)); + } + if (line.count () > 0 || x_count > 1) + antidote = DENORM (antidote.origin (), antidote.scale (), + line_m, line_c, segments, segs, using_row, row); + delete[]segs; + } + if (denorm != NULL) + *denorm = antidote; + //it's normalised + flags.set_bit (W_NORMALIZED, TRUE); +} + + +/********************************************************************** + * WERD::baseline_denormalise + * + * Baseline DeNormalise the word in Tesseract style. (I.e origin at centre of + * word at bottom. x-height region scaled to region y = + * (bln_baseline_offset)..(bln_baseline_offset + bln_x_height) + * - usually 64..192) + **********************************************************************/ + +void WERD::baseline_denormalise( // Tess style BL Norm + const DENORM *denorm //antidote + ) { + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + PBLOB *blob; + + if (!flags.bit (W_NORMALIZED)) { + WRONG_WORD.error ("WERD::baseline_denormalise", ABORT, + "Baseline normalised"); + } + + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + //denormalise it + blob->baseline_denormalise (denorm); + } + + //Repeat for rej blobs + blob_it.set_to_list ((PBLOB_LIST *) & rej_cblobs); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + //denormalise it + blob->baseline_denormalise (denorm); + } + + //it's not normalised + flags.set_bit (W_NORMALIZED, FALSE); +} + + +/********************************************************************** + * WERD::print + * + * Display members + **********************************************************************/ + +void WERD::print( //print + FILE * //file to print on + ) { + tprintf ("Blanks= %d\n", blanks); + bounding_box ().print (); + tprintf ("Flags = %d = 0%o\n", flags.val, flags.val); + tprintf (" W_SEGMENTED = %s\n", + flags.bit (W_SEGMENTED) ? "TRUE" : "FALSE "); + tprintf (" W_ITALIC = %s\n", flags.bit (W_ITALIC) ? "TRUE" : "FALSE "); + tprintf (" W_BOL = %s\n", flags.bit (W_BOL) ? "TRUE" : "FALSE "); + tprintf (" W_EOL = %s\n", flags.bit (W_EOL) ? "TRUE" : "FALSE "); + tprintf (" W_NORMALIZED = %s\n", + flags.bit (W_NORMALIZED) ? "TRUE" : "FALSE "); + tprintf (" W_POLYGON = %s\n", flags.bit (W_POLYGON) ? "TRUE" : "FALSE "); + tprintf (" W_LINEARC = %s\n", flags.bit (W_LINEARC) ? "TRUE" : "FALSE "); + tprintf (" W_DONT_CHOP = %s\n", + flags.bit (W_DONT_CHOP) ? "TRUE" : "FALSE "); + tprintf (" W_REP_CHAR = %s\n", + flags.bit (W_REP_CHAR) ? "TRUE" : "FALSE "); + tprintf (" W_FUZZY_SP = %s\n", + flags.bit (W_FUZZY_SP) ? "TRUE" : "FALSE "); + tprintf (" W_FUZZY_NON = %s\n", + flags.bit (W_FUZZY_NON) ? "TRUE" : "FALSE "); + tprintf ("Correct= %s\n", correct.string ()); + tprintf ("Rejected cblob count = %d\n", rej_cblobs.length ()); +} + + +/********************************************************************** + * WERD::plot + * + * Draw the WERD in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void WERD::plot( //draw it + WINDOW window, //window to draw in + COLOUR colour, //colour to draw in + BOOL8 solid //draw larcs solid + ) { + if (flags.bit (W_POLYGON)) { + //polygons + PBLOB_IT it = (PBLOB_LIST *) (&cblobs); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, colour, colour); + } + } + // else if (flags.bit(W_LINEARC)) + // { + // LARC_BLOB_IT it=(LARC_BLOB_LIST*)(&cblobs); + + // for ( it.mark_cycle_pt(); !it.cycled_list(); it.forward() ) + // { + // it.data()->plot(window,solid,colour,solid ? BLACK : colour); + // } + // } + else { + C_BLOB_IT it = &cblobs; //blobs of WERD + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, colour, colour); + } + } + plot_rej_blobs(window, solid); +} +#endif + + +/********************************************************************** + * WERD::plot + * + * Draw the WERD in rainbow colours. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void WERD::plot( //draw it + WINDOW window, //window to draw in + BOOL8 solid //draw larcs solid + ) { + COLOUR colour = FIRST_COLOUR; //current colour + if (flags.bit (W_POLYGON)) { + //polygons + PBLOB_IT it = (PBLOB_LIST *) (&cblobs); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, colour, CHILD_COLOUR); + colour = (COLOUR) (colour + 1); + if (colour == LAST_COLOUR) + colour = FIRST_COLOUR; //cycle round + } + } + // else if (flags.bit(W_LINEARC)) + // { + // LARC_BLOB_IT it=(LARC_BLOB_LIST*)(&cblobs); + + // for ( it.mark_cycle_pt(); !it.cycled_list(); it.forward() ) + // { + // it.data()->plot(window,solid,colour,solid ? BLACK : CHILD_COLOUR); + // colour=(COLOUR)(colour+1); + // if (colour==LAST_COLOUR) + // colour=FIRST_COLOUR; + // } + // } + else { + C_BLOB_IT it = &cblobs; //blobs of WERD + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, colour, CHILD_COLOUR); + colour = (COLOUR) (colour + 1); + if (colour == LAST_COLOUR) + colour = FIRST_COLOUR; //cycle round + } + } + plot_rej_blobs(window, solid); +} +#endif + + +/********************************************************************** + * WERD::plot_rej_blobs + * + * Draw the WERD rejected blobs - ALWAYS GREY + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void WERD::plot_rej_blobs( //draw it + WINDOW window, //window to draw in + BOOL8 solid //draw larcs solid + ) { + if (flags.bit (W_POLYGON)) { + PBLOB_IT it = (PBLOB_LIST *) (&rej_cblobs); + //polygons + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, GREY, GREY); + } + } + // else if (flags.bit(W_LINEARC)) + // { + // LARC_BLOB_IT it=(LARC_BLOB_LIST*)(&rej_cblobs); + + // for ( it.mark_cycle_pt(); !it.cycled_list(); it.forward() ) + // { + // it.data()->plot(window,solid,GREY,solid ? BLACK : GREY); + // } + // } + else { + C_BLOB_IT it = &rej_cblobs; //blobs of WERD + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, GREY, GREY); + } + } +} +#endif + + +/********************************************************************** + * WERD::shallow_copy() + * + * Make a shallow copy of a word + **********************************************************************/ + +WERD *WERD::shallow_copy() { //shallow copy + WERD *new_word = new WERD; + + new_word->blanks = blanks; + new_word->flags = flags; + new_word->dummy = dummy; + new_word->correct = correct; + return new_word; +} + + +/********************************************************************** + * WERD::operator= + * + * Assign a word, DEEP copying the blob list + **********************************************************************/ + +WERD & WERD::operator= ( //assign words +const WERD & source //from this +) { + this->ELIST_LINK::operator= (source); + blanks = source.blanks; + flags = source.flags; + dummy = source.dummy; + correct = source.correct; + if (flags.bit (W_POLYGON)) { + if (!cblobs.empty ()) + ((PBLOB_LIST *) (&cblobs))->clear (); + ((PBLOB_LIST *) (&cblobs))->deep_copy ((PBLOB_LIST *) (&source.cblobs)); + + if (!rej_cblobs.empty ()) + ((PBLOB_LIST *) (&rej_cblobs))->clear (); + ((PBLOB_LIST *) (&rej_cblobs))->deep_copy ((PBLOB_LIST *) (&source. + rej_cblobs)); + + } + // else if (flags.bit(W_LINEARC)) + // { + // if ( !cblobs.empty() ) + // ((LARC_BLOB_LIST*)(&cblobs))->clear(); + // ((LARC_BLOB_LIST*)(&cblobs))->deep_copy( + // (LARC_BLOB_LIST*)(&source.cblobs)); + + // if ( !rej_cblobs.empty() ) + // rej_cblobs.clear(); + // rej_cblobs.deep_copy( &source.rej_cblobs ); + // } + else { + if (!cblobs.empty ()) + cblobs.clear (); + cblobs.deep_copy (&source.cblobs); + + if (!rej_cblobs.empty ()) + rej_cblobs.clear (); + rej_cblobs.deep_copy (&source.rej_cblobs); + } + return *this; +} + + +/********************************************************************** + * word_comparator() + * + * word comparator used to sort a word list so that words are in increasing + * order of left edge. + **********************************************************************/ + +int word_comparator( //sort blobs + const void *word1p, //ptr to ptr to word1 + const void *word2p //ptr to ptr to word2 + ) { + WERD * + word1 = *(WERD **) word1p; + WERD * + word2 = *(WERD **) word2p; + + return word1->bounding_box ().left () - word2->bounding_box ().left (); +} diff --git a/ccstruct/werd.h b/ccstruct/werd.h new file mode 100644 index 0000000000..f35023d8a2 --- /dev/null +++ b/ccstruct/werd.h @@ -0,0 +1,277 @@ +/********************************************************************** + * File: word.c + * Description: Code for the WERD class. + * Author: Ray Smith + * Created: Tue Oct 08 14:32:12 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef WERD_H +#define WERD_H + +#include "varable.h" +#include "bits16.h" +#include "strngs.h" +#include "blckerr.h" +#include "stepblob.h" +#include "polyblob.h" +//#include "larcblob.h" + +enum WERD_FLAGS +{ + W_SEGMENTED, //correctly segmented + W_ITALIC, //italic text + W_BOLD, //bold text + W_BOL, //start of line + W_EOL, //end of line + W_NORMALIZED, //flags + W_POLYGON, //approximation + W_LINEARC, //linearc approx + W_DONT_CHOP, //fixed pitch chopped + W_REP_CHAR, //repeated character + W_FUZZY_SP, //fuzzy space + W_FUZZY_NON, //fuzzy nonspace + W_INVERSE //white on black +}; + +enum DISPLAY_FLAGS +{ + /* Display flags bit number allocations */ + DF_BOX, //Bounding box + DF_TEXT, //Correct ascii + DF_POLYGONAL, //Polyg approx + DF_EDGE_STEP, //Edge steps + DF_BN_POLYGONAL //BL normalisd polyapx +}; + +class ROW; //forward decl + +class WERD:public ELIST_LINK +{ + public: + WERD() { + } //empty constructor + WERD( //constructor + C_BLOB_LIST *blob_list, //blobs in word + UINT8 blanks, //blanks in front + const char *text); //correct text + WERD( //constructor + PBLOB_LIST *blob_list, //blobs in word + UINT8 blanks, //blanks in front + const char *text); //correct text + WERD( //constructor + PBLOB_LIST *blob_list, //blobs in word + WERD *clone); //use these flags etc. + WERD( //constructor + C_BLOB_LIST *blob_list, //blobs in word + WERD *clone); //use these flags etc. + ~WERD () { //destructor + if (flags.bit (W_POLYGON)) { + //use right destructor + ((PBLOB_LIST *) & cblobs)->clear (); + //use right destructor + ((PBLOB_LIST *) & rej_cblobs)->clear (); + } + // else if (flags.bit(W_LINEARC)) + // ((LARC_BLOB_LIST*)&cblobs)->clear(); //use right destructor + } + + WERD *poly_copy( //make copy as poly + float xheight); //row xheight + WERD *larc_copy( //make copy as larc + float xheight); //row xheight + + //get DUFF compact blobs + C_BLOB_LIST *rej_cblob_list() { + if (flags.bit (W_POLYGON)) + WRONG_WORD.error ("WERD::rej_cblob_list", ABORT, NULL); + return &rej_cblobs; + } + + //get DUFF poly blobs + PBLOB_LIST *rej_blob_list() { + if (!flags.bit (W_POLYGON)) + WRONG_WORD.error ("WERD::rej_blob_list", ABORT, NULL); + return (PBLOB_LIST *) (&rej_cblobs); + } + + C_BLOB_LIST *cblob_list() { //get compact blobs + if (flags.bit (W_POLYGON) || flags.bit (W_LINEARC)) + WRONG_WORD.error ("WERD::cblob_list", ABORT, NULL); + return &cblobs; + } + PBLOB_LIST *blob_list() { //get poly blobs + if (!flags.bit (W_POLYGON)) + WRONG_WORD.error ("WERD::blob_list", ABORT, NULL); + //make it right type + return (PBLOB_LIST *) (&cblobs); + } + // LARC_BLOB_LIST *larc_blob_list() //get poly blobs + // { + // if (!flags.bit(W_LINEARC)) + // WRONG_WORD.error("WERD::larc_blob_list",ABORT,NULL); + // return (LARC_BLOB_LIST*)(&cblobs); //make it right type + // } + PBLOB_LIST *gblob_list() { //get generic blobs + //make it right type + return (PBLOB_LIST *) (&cblobs); + } + + const char *text() const { //correct text + return correct.string (); + } + UINT8 space() { //access function + return blanks; + } + void set_blanks( //set blanks + UINT8 new_blanks) { + blanks = new_blanks; + } + + void set_text( //replace correct text + const char *new_text) { //with this + correct = new_text; + } + + BOX bounding_box(); //compute bounding box + + BOOL8 flag( //test flag + WERD_FLAGS mask) const { //flag to test + return flags.bit (mask); + } + void set_flag( //set flag value + WERD_FLAGS mask, //flag to test + BOOL8 value) { //value to set + flags.set_bit (mask, value); + } + + BOOL8 display_flag( //test display flag + UINT8 flag) const { //flag to test + return disp_flags.bit (flag); + } + + void set_display_flag( //set display flag + UINT8 flag, //flag to set + BOOL8 value) { //value to set + disp_flags.set_bit (flag, value); + } + + WERD *shallow_copy(); //shallow copy word + + void move( // reposition word + const ICOORD vec); // by vector + + void scale( // scale word + const float vec); // by multiplier + + void join_on( //append word + WERD *&other); //Deleting other + + void copy_on( //copy blobs + WERD *&other); //from other + + void baseline_normalise ( // Tess style BL Norm + //optional antidote + ROW * row, DENORM * denorm = NULL); + + void baseline_normalise_x ( //Use non standard xht + ROW * row, float x_height, //Weird value to use + DENORM * denorm = NULL); //optional antidote + + void baseline_denormalise( //un-normalise + const DENORM *denorm); + + void print( //print + FILE *fp); //file to print on + + void plot ( //draw one + WINDOW window, //window to draw in + //uniform colour + COLOUR colour, BOOL8 solid = FALSE); + + void plot ( //draw one + //in rainbow colours + WINDOW window, BOOL8 solid = FALSE); + + void plot_rej_blobs ( //draw one + //in rainbow colours + WINDOW window, BOOL8 solid = FALSE); + + WERD & operator= ( //assign words + const WERD & source); //from this + + void prep_serialise() { //set ptrs to counts + correct.prep_serialise (); + if (flags.bit (W_POLYGON)) + ((PBLOB_LIST *) (&cblobs))->prep_serialise (); + // else if (flags.bit(W_LINEARC)) + // ((LARC_BLOB_LIST*)(&cblobs))->prep_serialise(); + else + cblobs.prep_serialise (); + rej_cblobs.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + correct.dump (f); + if (flags.bit (W_POLYGON)) + ((PBLOB_LIST *) (&cblobs))->dump (f); + // else if (flags.bit(W_LINEARC)) + // ((LARC_BLOB_LIST*)(&cblobs))->dump( f ); + else + cblobs.dump (f); + rej_cblobs.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + correct.de_dump (f); + if (flags.bit (W_POLYGON)) + ((PBLOB_LIST *) (&cblobs))->de_dump (f); + // else if (flags.bit(W_LINEARC)) + // ((LARC_BLOB_LIST*)(&cblobs))->de_dump( f ); + else + cblobs.de_dump (f); + rej_cblobs.de_dump (f); + } + + make_serialise (WERD) private: + UINT8 blanks; //no of blanks + UINT8 dummy; //padding + BITS16 flags; //flags about word + BITS16 disp_flags; //display flags + INT16 dummy2; //padding + STRING correct; //correct text + C_BLOB_LIST cblobs; //compacted blobs + C_BLOB_LIST rej_cblobs; //DUFF blobs +}; + +ELISTIZEH_S (WERD) +#include "ocrrow.h" //placed here due to +extern BOOL_VAR_H (bln_numericmode, 0, "Optimize for numbers"); +extern INT_VAR_H (bln_x_height, 128, "Baseline Normalisation X-height"); +extern INT_VAR_H (bln_baseline_offset, 64, +"Baseline Norm. offset of baseline"); +//void poly_linearc_outlines( //do list of outlines +//LARC_OUTLINE_LIST *srclist, //list to convert +//OUTLINE_LIST *destlist //desstination list +//); +//OUTLINE *poly_larcline( //draw it +//LARC_OUTLINE *srcline //one to approximate +//); +int word_comparator( //sort blobs + const void *word1p, //ptr to ptr to word1 + const void *word2p //ptr to ptr to word2 + ); +#endif diff --git a/ccutil/Makefile.am b/ccutil/Makefile.am new file mode 100644 index 0000000000..b540b04a45 --- /dev/null +++ b/ccutil/Makefile.am @@ -0,0 +1,16 @@ +SUBDIRS = + +EXTRA_DIST = \ + basedir.h bits16.h clst.h debugwin.h elst2.h elst.h errcode.h \ + fileerr.h getopt.h globaloc.h hashfn.h host.h hosthplb.h lsterr.h \ + mainblk.h memblk.h memryerr.h memry.h mfcpch.h ndminx.h notdll.h \ + nwmain.h ocrclass.h ocrshell.h platform.h secname.h serialis.h \ + stderr.h strngs.h tessclas.h tprintf.h varable.h \ + mfcpch.cpp scanutils.cpp scanutils.h unichar.h + +noinst_LIBRARIES = libtesseract_ccutil.a +libtesseract_ccutil_a_SOURCES = \ + basedir.cpp bits16.cpp clst.cpp debugwin.cpp elst.cpp \ + elst2.cpp errcode.cpp globaloc.cpp hashfn.cpp mainblk.cpp \ + memblk.cpp memry.cpp ocrshell.cpp serialis.cpp strngs.cpp \ + tprintf.cpp varable.cpp unichar.cpp getopt.cpp diff --git a/ccutil/Makefile.in b/ccutil/Makefile.in new file mode 100644 index 0000000000..f6699a7885 --- /dev/null +++ b/ccutil/Makefile.in @@ -0,0 +1,556 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = ccutil +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_ccutil_a_AR = $(AR) $(ARFLAGS) +libtesseract_ccutil_a_LIBADD = +am_libtesseract_ccutil_a_OBJECTS = basedir.$(OBJEXT) bits16.$(OBJEXT) \ + clst.$(OBJEXT) debugwin.$(OBJEXT) elst.$(OBJEXT) \ + elst2.$(OBJEXT) errcode.$(OBJEXT) globaloc.$(OBJEXT) \ + hashfn.$(OBJEXT) mainblk.$(OBJEXT) memblk.$(OBJEXT) \ + memry.$(OBJEXT) ocrshell.$(OBJEXT) serialis.$(OBJEXT) \ + strngs.$(OBJEXT) tprintf.$(OBJEXT) varable.$(OBJEXT) \ + unichar.$(OBJEXT) getopt.$(OBJEXT) +libtesseract_ccutil_a_OBJECTS = $(am_libtesseract_ccutil_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_ccutil_a_SOURCES) +DIST_SOURCES = $(libtesseract_ccutil_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +EXTRA_DIST = \ + basedir.h bits16.h clst.h debugwin.h elst2.h elst.h errcode.h \ + fileerr.h getopt.h globaloc.h hashfn.h host.h hosthplb.h lsterr.h \ + mainblk.h memblk.h memryerr.h memry.h mfcpch.h ndminx.h notdll.h \ + nwmain.h ocrclass.h ocrshell.h platform.h secname.h serialis.h \ + stderr.h strngs.h tessclas.h tprintf.h varable.h \ + mfcpch.cpp scanutils.cpp scanutils.h unichar.h + +noinst_LIBRARIES = libtesseract_ccutil.a +libtesseract_ccutil_a_SOURCES = \ + basedir.cpp bits16.cpp clst.cpp debugwin.cpp elst.cpp \ + elst2.cpp errcode.cpp globaloc.cpp hashfn.cpp mainblk.cpp \ + memblk.cpp memry.cpp ocrshell.cpp serialis.cpp strngs.cpp \ + tprintf.cpp varable.cpp unichar.cpp getopt.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu ccutil/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu ccutil/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_ccutil.a: $(libtesseract_ccutil_a_OBJECTS) $(libtesseract_ccutil_a_DEPENDENCIES) + -rm -f libtesseract_ccutil.a + $(libtesseract_ccutil_a_AR) libtesseract_ccutil.a $(libtesseract_ccutil_a_OBJECTS) $(libtesseract_ccutil_a_LIBADD) + $(RANLIB) libtesseract_ccutil.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basedir.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bits16.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clst.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debugwin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elst.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elst2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errcode.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/globaloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hashfn.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainblk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memblk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrshell.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serialis.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strngs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unichar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/varable.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ccutil/basedir.cpp b/ccutil/basedir.cpp new file mode 100644 index 0000000000..64fb63d346 --- /dev/null +++ b/ccutil/basedir.cpp @@ -0,0 +1,104 @@ +/********************************************************************** + * File: basedir.c (Formerly getpath.c) + * Description: Find the directory location of the current executable using PATH. + * Author: Ray Smith + * Created: Mon Jul 09 09:06:39 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include "strngs.h" +#ifdef __UNIX__ +#include +#include +#else +#include +#endif +#include +#include "basedir.h" +#include "notdll.h" //must be last include + +/********************************************************************** + * getpath + * + * Find the directory of the given executable using the usual path rules. + * This enables data to be located relative to the code. + **********************************************************************/ + +DLLSYM INT8 getpath( //get dir name of code + const char *code, //executable to locate + STRING &path //output path name + ) { + char directory[MAX_PATH]; //main directory + #ifdef __UNIX__ + INT16 dirind; //index in directory + register char *pathlist; //$PATH + int fd; //file descriptor + + strcpy(directory, code); //get demo directory + dirind = strlen (directory); + while (dirind > 0 && directory[dirind - 1] != '/') + dirind--; //look back for dirname + directory[dirind] = '\0'; //end at directory + if (dirind != 0) { + path = directory; //had it in arg + return 0; + } + pathlist = getenv ("PATH"); //find search path + while (pathlist != NULL && *pathlist) { + for (dirind = 0; *pathlist != '\0' && *pathlist != ':';) + //copy a directory + directory[dirind++] = *pathlist++; + if (*pathlist == ':') + pathlist++; + if (dirind == 0) + continue; + if (directory[dirind - 1] != '/'); + directory[dirind++] = '/'; //add ending slash + directory[dirind++] = '\0'; + path = directory; //try this path + strcat(directory, code); + fd = open (directory, 0); + if (fd >= 0) { + close(fd); //found it + return 0; + } + } + strcpy (directory, "./"); + path = directory; //in current? + strcat(directory, code); + fd = open (directory, 0); + if (fd >= 0) { + close(fd); + return 0; //in current after all + } + return -1; + #endif + #ifdef __MSW32__ + char *path_end; //end of dir + + if (GetModuleFileName (NULL, directory, MAX_PATH - 1) == 0) { + return -1; + } + while ((path_end = strchr (directory, '\\')) != NULL) + *path_end = '/'; + path_end = strrchr (directory, '/'); + if (path_end != NULL) + path_end[1] = '\0'; + else + strcpy (directory, "./"); + path = directory; + return 0; + #endif +} diff --git a/ccutil/basedir.h b/ccutil/basedir.h new file mode 100644 index 0000000000..04f5fb9769 --- /dev/null +++ b/ccutil/basedir.h @@ -0,0 +1,32 @@ +/********************************************************************** + * File: basedir.h (Formerly getpath.h) + * Description: Header file for getpath.c. Provides relocatability of data. + * Author: Ray Smith + * Created: Mon Jul 09 09:13:03 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BASEDIR_H +#define BASEDIR_H + +#include "host.h" +#include "strngs.h" + +#include "notdll.h" //must be last include + +DLLSYM INT8 getpath( //get dir name of code + const char *code, //executable to locate + STRING &path //output path name + ); +#endif diff --git a/ccutil/bits16.cpp b/ccutil/bits16.cpp new file mode 100644 index 0000000000..2d90fe8593 --- /dev/null +++ b/ccutil/bits16.cpp @@ -0,0 +1,30 @@ +/********************************************************************** + * File: bits16.h (Formerly bits8.h) + * Description: Code for 8 bit field class. + * Author: Phil Cheatle + * Created: Thu Oct 17 10:10:05 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "bits16.h" + +/********************************************************************** + * Constructor. Something to get it past the compiler as almost all inlined. + * + **********************************************************************/ +BITS16::BITS16( // constructor + UINT16 init) { // initial val + val = init; +} diff --git a/ccutil/bits16.h b/ccutil/bits16.h new file mode 100644 index 0000000000..02070c19f4 --- /dev/null +++ b/ccutil/bits16.h @@ -0,0 +1,61 @@ +/********************************************************************** + * File: bits16.h (Formerly bits8.h) + * Description: Code for 8 bit field class. + * Author: Phil Cheatle + * Created: Thu Oct 17 10:10:05 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BITS16_H +#define BITS16_H + +#include "host.h" + +class DLLSYM BITS16 +{ + public: + UINT16 val; + + BITS16() { + val = 0; + } // constructor + + BITS16( // constructor + UINT16 init); // initial val + + void turn_on_bit( // flip specified bit + UINT8 bit_num) { // bit to flip 0..7 + val = val | 01 << bit_num; + }; + + void turn_off_bit( // flip specified bit + UINT8 bit_num) { // bit to flip 0..7 + val = val & ~(01 << bit_num); + }; + + void set_bit( // flip specified bit + UINT8 bit_num, // bit to flip 0..7 + BOOL8 value) { // value to flip to + if (value) + val = val | 01 << bit_num; + else + val = val & ~(01 << bit_num); + }; + + BOOL8 bit( // access bit + UINT8 bit_num) const { // bit to access + return (val >> bit_num) & 01; + }; +}; +#endif diff --git a/ccutil/clst.cpp b/ccutil/clst.cpp new file mode 100644 index 0000000000..5d03781858 --- /dev/null +++ b/ccutil/clst.cpp @@ -0,0 +1,591 @@ +/********************************************************************** + * File: clst.c (Formerly clist.c) + * Description: CONS cell list handling code which is not in the include file. + * Author: Phil Cheatle + * Created: Mon Jan 28 08:33:13 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include "clst.h" + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: CLIST + * ================================ + **********************************************************************/ + +/*********************************************************************** + * CLIST::internal_deep_clear + * + * Used by the "deep_clear" member function of derived list + * classes to destroy all the elements on the list. + * The calling function passes a "zapper" function which can be called to + * delete each data element of the list, regardless of its class. This + * technique permits a generic clear function to destroy elements of + * different derived types correctly, without requiring virtual functions and + * the consequential memory overhead. + **********************************************************************/ + +void +CLIST::internal_deep_clear ( //destroy all links +void (*zapper) (void *)) { //ptr to zapper functn + CLIST_LINK *ptr; + CLIST_LINK *next; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::internal_deep_clear", ABORT, NULL); + #endif + + if (!empty ()) { + ptr = last->next; //set to first + last->next = NULL; //break circle + last = NULL; //set list empty + while (ptr) { + next = ptr->next; + zapper (ptr->data); + delete(ptr); + ptr = next; + } + } +} + + +/*********************************************************************** + * CLIST::shallow_clear + * + * Used by the destructor and the "shallow_clear" member function of derived + * list classes to destroy the list. + * The data elements are NOT destroyed. + * + **********************************************************************/ + +void CLIST::shallow_clear() { //destroy all links + CLIST_LINK *ptr; + CLIST_LINK *next; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::shallow_clear", ABORT, NULL); + #endif + + if (!empty ()) { + ptr = last->next; //set to first + last->next = NULL; //break circle + last = NULL; //set list empty + while (ptr) { + next = ptr->next; + delete(ptr); + ptr = next; + } + } +} + + +/*********************************************************************** + * CLIST::internal_deep_copy + * + * Used during explict deep copy of a list. The "copier" function passed + * allows each element to be correctly deep copied (assuming that each class + * in the inheritance hierarchy does properly deep copies its members). The + * function passing technique is as for "internal_clear". + **********************************************************************/ + +void + //ptr to copier functn +CLIST::internal_deep_copy (void *(*copier) (void *), +const CLIST * list) { //list being copied + CLIST_ITERATOR from_it ((CLIST *) list); + CLIST_ITERATOR to_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::internal_deep_copy", ABORT, NULL); + if (!list) + BAD_PARAMETER.error ("CLIST::internal_deep_copy", ABORT, + "source list is NULL"); + #endif + + for (from_it.mark_cycle_pt (); !from_it.cycled_list (); from_it.forward ()) + to_it.add_after_then_move (copier (from_it.data ())); +} + + +/*********************************************************************** + * CLIST::assign_to_sublist + * + * The list is set to a sublist of another list. "This" list must be empty + * before this function is invoked. The two iterators passed must refer to + * the same list, different from "this" one. The sublist removed is the + * inclusive list from start_it's current position to end_it's current + * position. If this range passes over the end of the source list then the + * source list has its end set to the previous element of start_it. The + * extracted sublist is unaffected by the end point of the source list, its + * end point is always the end_it position. + **********************************************************************/ + +void CLIST::assign_to_sublist( //to this list + CLIST_ITERATOR *start_it, //from list start + CLIST_ITERATOR *end_it) { //from list end + const ERRCODE LIST_NOT_EMPTY = + "Destination list must be empty before extracting a sublist"; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::assign_to_sublist", ABORT, NULL); + #endif + + if (!empty ()) + LIST_NOT_EMPTY.error ("CLIST.assign_to_sublist", ABORT, NULL); + + last = start_it->extract_sublist (end_it); +} + + +/*********************************************************************** + * CLIST::length + * + * Return count of elements on list + **********************************************************************/ + +INT32 CLIST::length() { //count elements + CLIST_ITERATOR it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::length", ABORT, NULL); + #endif + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + count++; + return count; +} + + +/*********************************************************************** + * CLIST::sort + * + * Sort elements on list + **********************************************************************/ + +void +CLIST::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + CLIST_ITERATOR it(this); + INT32 count; + void **base; //ptr array to sort + void **current; + INT32 i; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::sort", ABORT, NULL); + #endif + + /* Allocate an array of pointers, one per list element */ + count = length (); + base = (void **) malloc (count * sizeof (void *)); + + /* Extract all elements, putting the pointers in the array */ + current = base; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + *current = it.extract (); + current++; + } + + /* Sort the pointer array */ + qsort ((char *) base, count, sizeof (*base), comparator); + + /* Rebuild the list from the sorted pointers */ + current = base; + for (i = 0; i < count; i++) { + it.add_to_end (*current); + current++; + } + free(base); +} + + +/*********************************************************************** + * CLIST::prep_serialise + * + * Replace the last member with a count of elements for serialisation. + * This is used on list objects which are members of objects being + * serialised. The containing object has been shallow copied and this member + * function is invoked on the COPY. + **********************************************************************/ + +void CLIST::prep_serialise() { + CLIST_ITERATOR this_it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::prep_serialise", ABORT, NULL); + #endif + + count = 0; + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + count++; + last = (CLIST_LINK *) count; +} + + +/*********************************************************************** + * CLIST::internal_dump + * + * Cause each element on the list to be serialised by walking the list and + * calling the element_serialiser function for each element. The + * element_serialiser simply does the appropriate coercion of the element to + * its real type and then invokes the elements serialise function + **********************************************************************/ + +void +CLIST::internal_dump (FILE * f, void element_serialiser (FILE *, void *)) { + CLIST_ITERATOR this_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::internal_dump", ABORT, NULL); + #endif + + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + element_serialiser (f, this_it.data ()); +} + + +/*********************************************************************** + * CLIST::internal_de_dump + * + * Cause each element on the list to be de_serialised by extracting the count + * of elements on the list, (held in the last member of the dumped version of + * the list object), and then de-serialising that number of list elements, + * adding each to the end of the reconstructed list. + **********************************************************************/ + +void +CLIST::internal_de_dump (FILE * f, void *element_de_serialiser (FILE *)) { + INT32 count = (ptrdiff_t) last; + CLIST_ITERATOR this_it; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::internal_de_dump", ABORT, NULL); + #endif + + last = NULL; + this_it.set_to_list (this); + for (; count > 0; count--) + this_it.add_to_end (element_de_serialiser (f)); +} + + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: CLIST_ITERATOR + * ========================================= + **********************************************************************/ + +/*********************************************************************** + * CLIST_ITERATOR::forward + * + * Move the iterator to the next element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + +void *CLIST_ITERATOR::forward() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::forward", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::forward", ABORT, NULL); + #endif + if (list->empty ()) + return NULL; + + if (current) { //not removed so + //set previous + prev = current; + started_cycling = TRUE; + } + else { + if (ex_current_was_cycle_pt) + cycle_pt = next; + } + current = next; + next = current->next; + + #ifdef _DEBUG + if (!current) + NULL_DATA.error ("CLIST_ITERATOR::forward", ABORT, NULL); + if (!next) + NULL_NEXT.error ("CLIST_ITERATOR::forward", ABORT, + "This is: %i Current is: %i", + (int) this, (int) current); + #endif + return current->data; +} + + +/*********************************************************************** + * CLIST_ITERATOR::data_relative + * + * Return the data pointer to the element "offset" elements from current. + * "offset" must not be less than -1. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + +void *CLIST_ITERATOR::data_relative( //get data + or - ... + INT8 offset) { //offset from current + CLIST_LINK *ptr; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); + if (offset < -1) + BAD_PARAMETER.error ("CLIST_ITERATOR::data_relative", ABORT, + "offset < -l"); + #endif + + if (offset == -1) + ptr = prev; + else + for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next); + + #ifdef _DEBUG + if (!ptr) + NULL_DATA.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); + #endif + + return ptr->data; +} + + +/*********************************************************************** + * CLIST_ITERATOR::move_to_last() + * + * Move current so that it is set to the end of the list. + * Return data just in case anyone wants it. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + +void *CLIST_ITERATOR::move_to_last() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::move_to_last", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::move_to_last", ABORT, NULL); + #endif + + while (current != list->last) + forward(); + + if (current == NULL) + return NULL; + else + return current->data; +} + + +/*********************************************************************** + * CLIST_ITERATOR::exchange() + * + * Given another iterator, whose current element is a different element on + * the same list list OR an element of another list, exchange the two current + * elements. On return, each iterator points to the element which was the + * other iterators current on entry. + * (This function hasn't been in-lined because its a bit big!) + **********************************************************************/ + +void CLIST_ITERATOR::exchange( //positions of 2 links + CLIST_ITERATOR *other_it) { //other iterator + const ERRCODE DONT_EXCHANGE_DELETED = + "Can't exchange deleted elements of lists"; + + CLIST_LINK *old_current; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::exchange", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::exchange", ABORT, NULL); + if (!other_it) + BAD_PARAMETER.error ("CLIST_ITERATOR::exchange", ABORT, "other_it NULL"); + if (!(other_it->list)) + NO_LIST.error ("CLIST_ITERATOR::exchange", ABORT, "other_it"); + #endif + + /* Do nothing if either list is empty or if both iterators reference the same + link */ + + if ((list->empty ()) || + (other_it->list->empty ()) || (current == other_it->current)) + return; + + /* Error if either current element is deleted */ + + if (!current || !other_it->current) + DONT_EXCHANGE_DELETED.error ("CLIST_ITERATOR.exchange", ABORT, NULL); + + /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements + (other before this); non-doubleton adjacent elements (this before other); + non-adjacent elements. */ + + //adjacent links + if ((next == other_it->current) || + (other_it->next == current)) { + //doubleton list + if ((next == other_it->current) && + (other_it->next == current)) { + prev = next = current; + other_it->prev = other_it->next = other_it->current; + } + else { //non-doubleton with + //adjacent links + //other before this + if (other_it->next == current) { + other_it->prev->next = current; + other_it->current->next = next; + current->next = other_it->current; + other_it->next = other_it->current; + prev = current; + } + else { //this before other + prev->next = other_it->current; + current->next = other_it->next; + other_it->current->next = current; + next = current; + other_it->prev = other_it->current; + } + } + } + else { //no overlap + prev->next = other_it->current; + current->next = other_it->next; + other_it->prev->next = current; + other_it->current->next = next; + } + + /* update end of list pointer when necessary (remember that the 2 iterators + may iterate over different lists!) */ + + if (list->last == current) + list->last = other_it->current; + if (other_it->list->last == other_it->current) + other_it->list->last = current; + + if (current == cycle_pt) + cycle_pt = other_it->cycle_pt; + if (other_it->current == other_it->cycle_pt) + other_it->cycle_pt = cycle_pt; + + /* The actual exchange - in all cases*/ + + old_current = current; + current = other_it->current; + other_it->current = old_current; +} + + +/*********************************************************************** + * CLIST_ITERATOR::extract_sublist() + * + * This is a private member, used only by CLIST::assign_to_sublist. + * Given another iterator for the same list, extract the links from THIS to + * OTHER inclusive, link them into a new circular list, and return a + * pointer to the last element. + * (Can't inline this function because it contains a loop) + **********************************************************************/ + +CLIST_LINK *CLIST_ITERATOR::extract_sublist( //from this current + CLIST_ITERATOR *other_it) { //to other current + CLIST_ITERATOR temp_it = *this; + CLIST_LINK *end_of_new_list; + + const ERRCODE BAD_SUBLIST = "Can't find sublist end point in original list"; + #ifdef _DEBUG + const ERRCODE BAD_EXTRACTION_PTS = + "Can't extract sublist from points on different lists"; + const ERRCODE DONT_EXTRACT_DELETED = + "Can't extract a sublist marked by deleted points"; + + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::extract_sublist", ABORT, NULL); + if (!other_it) + BAD_PARAMETER.error ("CLIST_ITERATOR::extract_sublist", ABORT, + "other_it NULL"); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::extract_sublist", ABORT, NULL); + if (list != other_it->list) + BAD_EXTRACTION_PTS.error ("CLIST_ITERATOR.extract_sublist", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("CLIST_ITERATOR::extract_sublist", ABORT, NULL); + + if (!current || !other_it->current) + DONT_EXTRACT_DELETED.error ("CLIST_ITERATOR.extract_sublist", ABORT, + NULL); + #endif + + ex_current_was_last = other_it->ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; + other_it->ex_current_was_cycle_pt = FALSE; + + temp_it.mark_cycle_pt (); + do { //walk sublist + if (temp_it.cycled_list ()) //cant find end pt + BAD_SUBLIST.error ("CLIST_ITERATOR.extract_sublist", ABORT, NULL); + + if (temp_it.at_last ()) { + list->last = prev; + ex_current_was_last = other_it->ex_current_was_last = TRUE; + } + + if (temp_it.current == cycle_pt) + ex_current_was_cycle_pt = TRUE; + + if (temp_it.current == other_it->cycle_pt) + other_it->ex_current_was_cycle_pt = TRUE; + + temp_it.forward (); + } + while (temp_it.prev != other_it->current); + + //circularise sublist + other_it->current->next = current; + end_of_new_list = other_it->current; + + //sublist = whole list + if (prev == other_it->current) { + list->last = NULL; + prev = current = next = NULL; + other_it->prev = other_it->current = other_it->next = NULL; + } + else { + prev->next = other_it->next; + current = other_it->current = NULL; + next = other_it->next; + other_it->prev = prev; + } + return end_of_new_list; +} diff --git a/ccutil/clst.h b/ccutil/clst.h new file mode 100644 index 0000000000..71161e97f5 --- /dev/null +++ b/ccutil/clst.h @@ -0,0 +1,1078 @@ +/********************************************************************** + * File: clst.h (Formerly clist.h) + * Description: CONS cell list module include file. + * Author: Phil Cheatle + * Created: Mon Jan 28 08:33:13 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef CLST_H +#define CLST_H + +#include +#include "host.h" +#include "serialis.h" +#include "lsterr.h" + +class CLIST_ITERATOR; + +/********************************************************************** + * CLASS - CLIST_LINK + * + * Generic link class for singly linked CONS cell lists + * + * Note: No destructor - elements are assumed to be destroyed EITHER after + * they have been extracted from a list OR by the CLIST destructor which + * walks the list. + **********************************************************************/ + +class DLLSYM CLIST_LINK +{ + friend class CLIST_ITERATOR; + friend class CLIST; + + CLIST_LINK *next; + void *data; + + public: + CLIST_LINK() { //constructor + data = next = NULL; + } + + CLIST_LINK( //copy constructor + const CLIST_LINK &) { //dont copy link + data = next = NULL; + } + + void operator= ( //dont copy links + const CLIST_LINK &) { + data = next = NULL; + } + + NEWDELETE2 (CLIST_LINK) + /* NOTE that none of the serialise member functions are required for + CLIST_LINKS as they are never serialised. */ +}; + +/********************************************************************** + * CLASS - CLIST + * + * Generic list class for singly linked CONS cell lists + **********************************************************************/ + +class DLLSYM CLIST +{ + friend class CLIST_ITERATOR; + + CLIST_LINK *last; //End of list + //(Points to head) + CLIST_LINK *First() { // return first + return last != NULL ? last->next : NULL; + } + + public: + CLIST() { //constructor + last = NULL; + } + + ~CLIST () { //destructor + shallow_clear(); + } + + void internal_deep_clear ( //destroy all links + void (*zapper) (void *)); //ptr to zapper functn + + void shallow_clear(); //clear list but dont + //delete data elements + + BOOL8 empty() { //is list empty? + return !last; + } + + BOOL8 singleton() { + return last != NULL ? (last == last->next) : FALSE; + } + + void shallow_copy( //dangerous!! + CLIST *from_list) { //beware destructors!! + last = from_list->last; + } + + //ptr to copier functn + void internal_deep_copy (void *(*copier) (void *), + const CLIST * list); //list being copied + + void assign_to_sublist( //to this list + CLIST_ITERATOR *start_it, //from list start + CLIST_ITERATOR *end_it); //from list end + + INT32 length(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + + void internal_dump ( //serialise each elem + FILE * f, //to this file + void element_serialiser ( //using this function + FILE *, void *)); + + void internal_de_dump ( //de_serial each elem + FILE * f, //from this file + void *element_de_serialiser (//using this function + FILE *)); + + void prep_serialise(); //change last to count + + /* Note that dump() and de_dump() are not required as calls to dump/de_dump a + list class should be handled by a class derived from this. + + make_serialise is not required for a similar reason. + */ +}; + +/*********************************************************************** + * CLASS - CLIST_ITERATOR + * + * Generic iterator class for singly linked lists with embedded links + **********************************************************************/ + +class DLLSYM CLIST_ITERATOR +{ + friend void CLIST::assign_to_sublist(CLIST_ITERATOR *, CLIST_ITERATOR *); + + CLIST *list; //List being iterated + CLIST_LINK *prev; //prev element + CLIST_LINK *current; //current element + CLIST_LINK *next; //next element + BOOL8 ex_current_was_last; //current extracted + //was end of list + BOOL8 ex_current_was_cycle_pt; //current extracted + //was cycle point + CLIST_LINK *cycle_pt; //point we are cycling + //the list to. + BOOL8 started_cycling; //Have we moved off + //the start? + + CLIST_LINK *extract_sublist( //from this current... + CLIST_ITERATOR *other_it); //to other current + + public: + CLIST_ITERATOR() { //constructor + list = NULL; + } //unassigned list + + CLIST_ITERATOR( //constructor + CLIST *list_to_iterate); + + void set_to_list( //change list + CLIST *list_to_iterate); + + void add_after_then_move( //add after current & + void *new_data); //move to new + + void add_after_stay_put( //add after current & + void *new_data); //stay at current + + void add_before_then_move( //add before current & + void *new_data); //move to new + + void add_before_stay_put( //add before current & + void *new_data); //stay at current + + void add_list_after( //add a list & + CLIST *list_to_add); //stay at current + + void add_list_before( //add a list & + CLIST *list_to_add); //move to it 1st item + + void *data() { //get current data + #ifdef _DEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::data", ABORT, NULL); + if (!current) + NULL_DATA.error ("CLIST_ITERATOR::data", ABORT, NULL); + #endif + return current->data; + } + + void *data_relative( //get data + or - ... + INT8 offset); //offset from current + + void *forward(); //move to next element + + void *extract(); //remove from list + + void *move_to_first(); //go to start of list + + void *move_to_last(); //go to end of list + + void mark_cycle_pt(); //remember current + + BOOL8 empty() { //is list empty? + #ifdef _DEBUG + if (!list) + NO_LIST.error ("CLIST_ITERATOR::empty", ABORT, NULL); + #endif + return list->empty (); + } + + BOOL8 current_extracted() { //current extracted? + return !current; + } + + BOOL8 at_first(); //Current is first? + + BOOL8 at_last(); //Current is last? + + BOOL8 cycled_list(); //Completed a cycle? + + void add_to_end( //add at end & + void *new_data); //dont move + + void exchange( //positions of 2 links + CLIST_ITERATOR *other_it); //other iterator + + INT32 length(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + +}; + +/*********************************************************************** + * CLIST_ITERATOR::set_to_list + * + * (Re-)initialise the iterator to point to the start of the list_to_iterate + * over. + **********************************************************************/ + +inline void CLIST_ITERATOR::set_to_list( //change list + CLIST *list_to_iterate) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::set_to_list", ABORT, NULL); + if (!list_to_iterate) + BAD_PARAMETER.error ("CLIST_ITERATOR::set_to_list", ABORT, + "list_to_iterate is NULL"); + #endif + + list = list_to_iterate; + prev = list->last; + current = list->First (); + next = current != NULL ? current->next : NULL; + cycle_pt = NULL; //await explicit set + started_cycling = FALSE; + ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; +} + + +/*********************************************************************** + * CLIST_ITERATOR::CLIST_ITERATOR + * + * CONSTRUCTOR - set iterator to specified list; + **********************************************************************/ + +inline CLIST_ITERATOR::CLIST_ITERATOR(CLIST *list_to_iterate) { + set_to_list(list_to_iterate); +} + + +/*********************************************************************** + * CLIST_ITERATOR::add_after_then_move + * + * Add a new element to the list after the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_after_then_move( // element to add + void *new_data) { + CLIST_LINK *new_element; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_after_then_move", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_after_then_move", ABORT, NULL); + if (!new_data) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_after_then_move", ABORT, + "new_data is NULL"); + #endif + + new_element = new CLIST_LINK; + new_element->data = new_data; + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + new_element->next = next; + + if (current) { //not extracted + current->next = new_element; + prev = current; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + prev->next = new_element; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + + +/*********************************************************************** + * CLIST_ITERATOR::add_after_stay_put + * + * Add a new element to the list after the current element but do not move + * the iterator to the new element. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_after_stay_put( // element to add + void *new_data) { + CLIST_LINK *new_element; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_after_stay_put", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_after_stay_put", ABORT, NULL); + if (!new_data) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_after_stay_put", ABORT, + "new_data is NULL"); + #endif + + new_element = new CLIST_LINK; + new_element->data = new_data; + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = FALSE; + current = NULL; + } + else { + new_element->next = next; + + if (current) { //not extracted + current->next = new_element; + if (prev == current) + prev = new_element; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + ex_current_was_last = FALSE; + } + } + next = new_element; + } +} + + +/*********************************************************************** + * CLIST_ITERATOR::add_before_then_move + * + * Add a new element to the list before the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_before_then_move( // element to add + void *new_data) { + CLIST_LINK *new_element; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_before_then_move", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_before_then_move", ABORT, NULL); + if (!new_data) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_before_then_move", ABORT, + "new_data is NULL"); + #endif + + new_element = new CLIST_LINK; + new_element->data = new_data; + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + prev->next = new_element; + if (current) { //not extracted + new_element->next = current; + next = current; + } + else { //current extracted + new_element->next = next; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + + +/*********************************************************************** + * CLIST_ITERATOR::add_before_stay_put + * + * Add a new element to the list before the current element but dont move the + * iterator to the new element. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_before_stay_put( // element to add + void *new_data) { + CLIST_LINK *new_element; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_before_stay_put", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_before_stay_put", ABORT, NULL); + if (!new_data) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_before_stay_put", ABORT, + "new_data is NULL"); + #endif + + new_element = new CLIST_LINK; + new_element->data = new_data; + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = TRUE; + current = NULL; + } + else { + prev->next = new_element; + if (current) { //not extracted + new_element->next = current; + if (next == current) + next = new_element; + } + else { //current extracted + new_element->next = next; + if (ex_current_was_last) + list->last = new_element; + } + prev = new_element; + } +} + + +/*********************************************************************** + * CLIST_ITERATOR::add_list_after + * + * Insert another list to this list after the current element but dont move the + * iterator. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_list_after(CLIST *list_to_add) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_list_after", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_list_after", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_list_after", ABORT, + "list_to_add is NULL"); + #endif + + if (!list_to_add->empty ()) { + if (list->empty ()) { + list->last = list_to_add->last; + prev = list->last; + next = list->First (); + ex_current_was_last = TRUE; + current = NULL; + } + else { + if (current) { //not extracted + current->next = list_to_add->First (); + if (current == list->last) + list->last = list_to_add->last; + list_to_add->last->next = next; + next = current->next; + } + else { //current extracted + prev->next = list_to_add->First (); + if (ex_current_was_last) { + list->last = list_to_add->last; + ex_current_was_last = FALSE; + } + list_to_add->last->next = next; + next = prev->next; + } + } + list_to_add->last = NULL; + } +} + + +/*********************************************************************** + * CLIST_ITERATOR::add_list_before + * + * Insert another list to this list before the current element. Move the + * iterator to the start of the inserted elements + * iterator. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_list_before(CLIST *list_to_add) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_list_before", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_list_before", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_list_before", ABORT, + "list_to_add is NULL"); + #endif + + if (!list_to_add->empty ()) { + if (list->empty ()) { + list->last = list_to_add->last; + prev = list->last; + current = list->First (); + next = current->next; + ex_current_was_last = FALSE; + } + else { + prev->next = list_to_add->First (); + if (current) { //not extracted + list_to_add->last->next = current; + } + else { //current extracted + list_to_add->last->next = next; + if (ex_current_was_last) + list->last = list_to_add->last; + if (ex_current_was_cycle_pt) + cycle_pt = prev->next; + } + current = prev->next; + next = current->next; + } + list_to_add->last = NULL; + } +} + + +/*********************************************************************** + * CLIST_ITERATOR::extract + * + * Do extraction by removing current from the list, deleting the cons cell + * and returning the data to the caller, but NOT updating the iterator. (So + * that any calling loop can do this.) The iterator's current points to + * NULL. If the data is to be deleted, this is the callers responsibility. + **********************************************************************/ + +inline void *CLIST_ITERATOR::extract() { + void *extracted_data; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::extract", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::extract", ABORT, NULL); + if (!current) //list empty or + //element extracted + NULL_CURRENT.error ("CLIST_ITERATOR::extract", + ABORT, NULL); + #endif + + if (list->singleton ()) //special case where + //we do need to + prev = next = list->last = NULL; + // change the iterator + else { + prev->next = next; //remove from list + + if (current == list->last) { + list->last = prev; + ex_current_was_last = TRUE; + } + else + ex_current_was_last = FALSE; + + ex_current_was_cycle_pt = (current == cycle_pt) ? TRUE : FALSE; + + } + extracted_data = current->data; + delete(current); //destroy CONS cell + current = NULL; + return extracted_data; +} + + +/*********************************************************************** + * CLIST_ITERATOR::move_to_first() + * + * Move current so that it is set to the start of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + +inline void *CLIST_ITERATOR::move_to_first() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::move_to_first", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::move_to_first", ABORT, NULL); + #endif + + current = list->First (); + prev = list->last; + next = current != NULL ? current->next : NULL; + return current->data; +} + + +/*********************************************************************** + * CLIST_ITERATOR::mark_cycle_pt() + * + * Remember the current location so that we can tell whether we've returned + * to this point later. + * + * If the current point is deleted either now, or in the future, the cycle + * point will be set to the next item which is set to current. This could be + * by a forward, add_after_then_move or add_after_then_move. + **********************************************************************/ + +inline void CLIST_ITERATOR::mark_cycle_pt() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::mark_cycle_pt", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::mark_cycle_pt", ABORT, NULL); + #endif + + if (current) + cycle_pt = current; + else + ex_current_was_cycle_pt = TRUE; + started_cycling = FALSE; +} + + +/*********************************************************************** + * CLIST_ITERATOR::at_first() + * + * Are we at the start of the list? + * + **********************************************************************/ + +inline BOOL8 CLIST_ITERATOR::at_first() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::at_first", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::at_first", ABORT, NULL); + #endif + + //we're at a deleted + return ((list->empty ()) || (current == list->First ()) || ((current == NULL) && + (prev == list->last) && //NON-last pt between + !ex_current_was_last)); //first and last +} + + +/*********************************************************************** + * CLIST_ITERATOR::at_last() + * + * Are we at the end of the list? + * + **********************************************************************/ + +inline BOOL8 CLIST_ITERATOR::at_last() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::at_last", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::at_last", ABORT, NULL); + #endif + + //we're at a deleted + return ((list->empty ()) || (current == list->last) || ((current == NULL) && + (prev == list->last) && //last point between + ex_current_was_last)); //first and last +} + + +/*********************************************************************** + * CLIST_ITERATOR::cycled_list() + * + * Have we returned to the cycle_pt since it was set? + * + **********************************************************************/ + +inline BOOL8 CLIST_ITERATOR::cycled_list() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::cycled_list", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::cycled_list", ABORT, NULL); + #endif + + return ((list->empty ()) || ((current == cycle_pt) && started_cycling)); + +} + + +/*********************************************************************** + * CLIST_ITERATOR::length() + * + * Return the length of the list + * + **********************************************************************/ + +inline INT32 CLIST_ITERATOR::length() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::length", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::length", ABORT, NULL); + #endif + + return list->length (); +} + + +/*********************************************************************** + * CLIST_ITERATOR::sort() + * + * Sort the elements of the list, then reposition at the start. + * + **********************************************************************/ + +inline void +CLIST_ITERATOR::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::sort", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::sort", ABORT, NULL); + #endif + + list->sort (comparator); + move_to_first(); +} + + +/*********************************************************************** + * CLIST_ITERATOR::add_to_end + * + * Add a new element to the end of the list without moving the iterator. + * This is provided because a single linked list cannot move to the last as + * the iterator couldn't set its prev pointer. Adding to the end is + * essential for implementing + queues. +**********************************************************************/ + +inline void CLIST_ITERATOR::add_to_end( // element to add + void *new_data) { + CLIST_LINK *new_element; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_to_end", ABORT, NULL); + if (!list) + NO_LIST.error ("CLIST_ITERATOR::add_to_end", ABORT, NULL); + if (!new_data) + BAD_PARAMETER.error ("CLIST_ITERATOR::add_to_end", ABORT, + "new_data is NULL"); + #endif + + if (this->at_last ()) { + this->add_after_stay_put (new_data); + } + else { + if (this->at_first ()) { + this->add_before_stay_put (new_data); + list->last = prev; + } + else { //Iteratr is elsewhere + new_element = new CLIST_LINK; + new_element->data = new_data; + + new_element->next = list->last->next; + list->last->next = new_element; + list->last = new_element; + } + } +} + + +/*********************************************************************** + QUOTE_IT MACRO DEFINITION + =========================== +Replace with "". may be an arbitrary number of tokens +***********************************************************************/ + +#define QUOTE_IT( parm ) #parm + +/*********************************************************************** + CLISTIZE( CLASSNAME ) MACRO DEFINITION + ====================================== + +CLASSNAME is assumed to be the name of a class to be used in a CONS list + +NOTE: Because we dont use virtual functions in the list code, the list code +will NOT work correctly for classes derived from this. + +The macro generates: + - An element deletion function: CLASSNAME##_c1_zapper + - An element copier function: + CLASSNAME##_c1_copier + - An element serialiser function" CLASSNAME##_c1_serialiser + - An element de-serialiser function" CLASSNAME##_c1_de_serialiser + - A CLIST subclass: CLASSNAME##_CLIST + - A CLIST_ITERATOR subclass: + CLASSNAME##_C_IT + +NOTE: Generated names do NOT clash with those generated by ELISTIZE, +ELIST2ISE and CLIST2IZE + +Four macros are provided: CLISTIZE, CLISTIZE_S, CLISTIZEH and CLISTIZEH_S +The ...IZEH macros just define the class names for use in .h files +The ...IZE macros define the code use in .c files +The _S versions define lists which can be serialised. They assume that the +make_serialise() macro is used in the list element class to define +serialise() and de_serialise() members for the list elements. +This, in turn, assumes that the list element class has prep_serialise() +dump() and de_dump() member functions. +***********************************************************************/ + +/*********************************************************************** + CLISTIZEH( CLASSNAME ) and CLISTIZEH_S( CLASSNAME ) MACROS + +These macros are constructed from 3 fragments CLISTIZEH_A, CLISTIZEH_B and +CLISTIZEH_C. CLISTIZEH is simply a concatenation of these parts. +CLISTIZEH_S has some additional bits thrown in the gaps. +***********************************************************************/ + +#define CLISTIZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_c1_zapper( /*delete a link*/ \ +void* link); /*link to delete*/ \ + \ +extern DLLSYM void* CLASSNAME##_c1_copier( /*deep copy a link*/ \ +void* old_element); /*source link */ + +#define CLISTIZEH_B( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASS - CLASSNAME##_CLIST \ +* \ +* List class for class CLASSNAME \ +* \ +**********************************************************************/ \ + \ +class DLLSYM CLASSNAME##_CLIST : public CLIST \ +{ \ +public: \ + CLASSNAME##_CLIST():CLIST() {} \ + /* constructor */ \ + \ + CLASSNAME##_CLIST( /* dont construct */ \ + const CLASSNAME##_CLIST&) /*by initial assign*/ \ + { DONT_CONSTRUCT_LIST_BY_COPY.error( QUOTE_IT( CLASSNAME##_CLIST ), \ + ABORT, NULL ); } \ + \ +void deep_clear() /* delete elements */ \ + { CLIST::internal_deep_clear( &CLASSNAME##_c1_zapper ); } \ + \ +void deep_copy( /* become a deep */ \ + const CLASSNAME##_CLIST*list) /* copy of src list*/ \ + { CLIST::internal_deep_copy( &CLASSNAME##_c1_copier, list ); } \ + \ +void operator=( /* prevent assign */ \ + const CLASSNAME##_CLIST&) \ + { DONT_ASSIGN_LISTS.error( QUOTE_IT( CLASSNAME##_CLIST ), \ + ABORT, NULL ); } + +#define CLISTIZEH_C( CLASSNAME ) \ + \ +}; \ + \ + \ + \ +/*********************************************************************** \ +* CLASS - CLASSNAME##_C_IT \ +* \ +* Iterator class for class CLASSNAME##_CLIST \ +* \ +* Note: We don't need to coerce pointers to member functions input \ +* parameters as these are automatically converted to the type of the base \ +* type. ("A ptr to a class may be converted to a pointer to a public base \ +* class of that class") \ +**********************************************************************/ \ + \ +class DLLSYM CLASSNAME##_C_IT : public CLIST_ITERATOR \ +{ \ +public: \ + CLASSNAME##_C_IT():CLIST_ITERATOR(){} \ + \ + CLASSNAME##_C_IT( \ + CLASSNAME##_CLIST* list):CLIST_ITERATOR(list){} \ + \ + CLASSNAME* data() \ + { return (CLASSNAME*) CLIST_ITERATOR::data(); } \ + \ + CLASSNAME* data_relative( \ + INT8 offset) \ + { return (CLASSNAME*) CLIST_ITERATOR::data_relative( offset ); } \ + \ + CLASSNAME* forward() \ + { return (CLASSNAME*) CLIST_ITERATOR::forward(); } \ + \ + CLASSNAME* extract() \ + { return (CLASSNAME*) CLIST_ITERATOR::extract(); } \ + \ + CLASSNAME* move_to_first() \ + { return (CLASSNAME*) CLIST_ITERATOR::move_to_first(); } \ + \ + CLASSNAME* move_to_last() \ + { return (CLASSNAME*) CLIST_ITERATOR::move_to_last(); } \ +}; + +#define CLISTIZEH( CLASSNAME ) \ + \ +CLISTIZEH_A( CLASSNAME ) \ + \ +CLISTIZEH_B( CLASSNAME ) \ + \ +CLISTIZEH_C( CLASSNAME ) + +#define CLISTIZEH_S( CLASSNAME ) \ + \ +CLISTIZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_c1_serialiser( \ +FILE* f, \ +void* element); \ + \ +extern DLLSYM void* CLASSNAME##_c1_de_serialiser( \ +FILE* f); \ + \ +CLISTIZEH_B( CLASSNAME ) \ + \ + void dump( /* dump to file */ \ + FILE* f) \ + { CLIST::internal_dump( f, &CLASSNAME##_c1_serialiser );} \ + \ + void de_dump( /* get from file */ \ + FILE* f) \ + { CLIST::internal_de_dump( f, &CLASSNAME##_c1_de_serialiser );} \ + \ +make_serialise( CLASSNAME##_CLIST ) \ + \ +CLISTIZEH_C( CLASSNAME ) + +/*********************************************************************** + CLISTIZE( CLASSNAME ) and CLISTIZE_S( CLASSNAME ) MACROS +CLISTIZE_S is a simple extension to CLISTIZE +***********************************************************************/ + +#define CLISTIZE( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASSNAME##_c1_zapper \ +* \ +* A function which can delete a CLASSNAME element. This is passed to the \ +* generic deep_clear list member function so that when a list is cleared the \ +* elements on the list are properly destroyed from the base class, even \ +* though we dont use a virtual destructor function. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_c1_zapper( /*delete a link*/ \ +void* link) /*link to delete*/ \ +{ \ +delete (CLASSNAME *) link; \ +} \ + \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_c1_copier \ +* \ +* A function which can generate a new, deep copy of a CLASSNAME element. \ +* This is passed to the generic deep copy list member function so that when \ +* a list is copied the elements on the list are properly copied from the \ +* base class, even though we dont use a virtual function. \ +* \ +**********************************************************************/ \ + \ +DLLSYM void* CLASSNAME##_c1_copier( /*deep copy a link*/ \ +void* old_element) /*source link*/ \ +{ \ + CLASSNAME* new_element; \ + \ +new_element = new CLASSNAME; \ +*new_element = *((CLASSNAME*) old_element); \ +return (void*) new_element; \ +} + +#define CLISTIZE_S( CLASSNAME ) \ + \ +CLISTIZE( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASSNAME##_c1_serialiser \ +* \ +* A function which can serialise an element \ +* This is passed to the generic dump member function so that when a list is \ +* serialised the elements on the list are properly serialised. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_c1_serialiser( \ +FILE* f, \ +void* element) \ +{ \ +((CLASSNAME*) element)->serialise( f ); \ +} \ + \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_c1_de_serialiser \ +* \ +* A function which can de-serialise an element \ +* This is passed to the generic de-dump member function so that when a list \ +* is de-serialised the elements on the list are properly de-serialised. \ +**********************************************************************/ \ + \ +DLLSYM void* CLASSNAME##_c1_de_serialiser( \ +FILE* f) \ +{ \ +return CLASSNAME::de_serialise( f ); \ +} +#endif diff --git a/ccutil/debugwin.cpp b/ccutil/debugwin.cpp new file mode 100644 index 0000000000..f1a115828a --- /dev/null +++ b/ccutil/debugwin.cpp @@ -0,0 +1,500 @@ +/********************************************************************** + * File: debugwin.cpp + * Description: Portable debug window class. + * Author: Ray Smith + * Created: Wed Feb 21 15:36:59 MST 1996 + * + * (C) Copyright 1996, Hewlett-Packard Co. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include "debugwin.h" + +DLLSYM INT_VAR (debug_lines, 256, "Number of lines in debug window"); + +#ifndef GRAPHICS_DISABLED + +#ifdef __MAC__ +#include +#include +//#include + +#define scrl_SCROLLER 101 +#define text_FLOWED 100 + +static LCommander *pCommander = NULL; +#endif + + //NT implementation +#if defined(__MSW32__) && !defined(_CONSOLE) + +#include +#define ID_DEBUG_MSG 32779 + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Create a debug window with size according to the arguments. + **********************************************************************/ + +DEBUG_WIN::DEBUG_WIN( //constructor + const char *title, //of window + INT32 xpos, //initial position + INT32 ypos, //in pixels + INT32 xsize, //initial size + INT32 ysize, //in pixels + INT32 buflines //default scroll size + ) { + char cmd[1024]; + int parm; //output from scrolrwin + STARTUPINFO start_info; //process control + PROCESS_INFORMATION proc_info; //process ids + SECURITY_ATTRIBUTES security; //for handles + + handle = NULL; + shm_hand = NULL; + shm_mem = NULL; + msg_end = NULL; + dbg_process = NULL; //save handles + dbg_thread = NULL; + security.nLength = sizeof (security); + security.lpSecurityDescriptor = NULL; + security.bInheritHandle = TRUE;//make it inheritable + //anonymous + shm_hand = CreateFileMapping ((HANDLE) 0xffffffff, &security, PAGE_READWRITE, 0, 4096, NULL); + if (shm_hand == NULL) + return; //failed + shm_mem = (char *) MapViewOfFile (shm_hand, FILE_MAP_WRITE, 0, 0, 0); + if (shm_mem == NULL) + return; + shm_mem[5] = 0; + sprintf (cmd, "scrolwin.exe %d %d", buflines, shm_hand); + GetStartupInfo(&start_info); //clone our stuff + if (!CreateProcess (NULL, cmd, NULL, NULL, TRUE, + CREATE_NO_WINDOW | DETACHED_PROCESS | CREATE_SUSPENDED, + NULL, NULL, &start_info, &proc_info)) + return; + + //save handles + dbg_process = proc_info.hProcess; + dbg_thread = proc_info.hThread; + if (ResumeThread (dbg_thread) != 1) + return; + do + Sleep (100); + while (shm_mem[5] == 0); //wait for handle + parm = ((((UINT8) shm_mem[4] << 8) + (UINT8) shm_mem[3] << 8) + + (UINT8) shm_mem[2] << 8) + (UINT8) shm_mem[1]; + handle = (HWND) parm; + if (handle != NULL) { + //setup window + ::SetWindowText (handle, title); + ::MoveWindow (handle, xpos, ypos, xsize, ysize, TRUE); + ::ShowWindow (handle, SW_SHOW); + } +} + + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Destroy a debug window. + **********************************************************************/ + +DEBUG_WIN::~DEBUG_WIN ( +//destructor +) { + if (IsWindow (handle)) + ::SendMessage (handle, WM_COMMAND, IDOK, 0); + if (shm_mem != NULL) + UnmapViewOfFile(shm_mem); + if (shm_hand != NULL) + CloseHandle(shm_hand); + if (dbg_thread != NULL) + CloseHandle(dbg_thread); + if (dbg_process == NULL) + CloseHandle(dbg_process); + +} + + +/********************************************************************** + * dprintf + * + * Print a message to the debug window. + * Like printf, this function can cope with messages which do not end + * in newline, but nothing is printed until the newline is received. + **********************************************************************/ + +void +DEBUG_WIN::dprintf ( //debug printf +const char *format, ... //special message +) { + va_list args; //variable args + char *msg_start; //for printing + + if (!IsWindow (handle)) + return; //destroyed + if (msg_end == NULL) + msg_end = shm_mem + 1; + va_start(args, format); //variable list + //Format into msg + vsprintf(msg_end, format, args); + va_end(args); + if (*msg_end == '\0') + return; + msg_start = shm_mem + 1; + do { + //end of line + msg_end = strchr (msg_start, '\n'); + if (msg_end == NULL) { + if (msg_start != shm_mem + 1) + //bring to front + strcpy (shm_mem + 1, msg_start); + //current end + msg_end = shm_mem + 1 + strlen (shm_mem + 1); + return; + } + *msg_end = '\0'; + while (IsWindow (handle) && shm_mem[0]) + Sleep (500); + if (IsWindow (handle)) { + //Visual C++2.0 macro + ::SendMessage (handle, WM_COMMAND, ID_DEBUG_MSG, (DWORD) (msg_start - shm_mem)); + } + msg_start = msg_end + 1; + } + while (*msg_start != '\0'); + msg_end = shm_mem + 1; //buffer empty +} + + +/********************************************************************** + * await_destruction + * + * Wait for the user to close the debug window. Then return. + **********************************************************************/ + +void DEBUG_WIN::await_destruction() { //wait for user to close + WaitForSingleObject (dbg_process, (unsigned long) -1); +} +#endif //NT Implmentation + + //UNIX implementation +#if defined(__UNIX__) || defined(_CONSOLE) +#ifdef __UNIX__ +#include +#include +#endif +//#include "basefile.h" + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Create a debug window with size according to the arguments. + * Create an hpterm window with a pipe connected to it. + **********************************************************************/ + +DEBUG_WIN::DEBUG_WIN( //constructor + const char *title, //of window + INT32 xpos, //initial position + INT32 ypos, //in pixels + INT32 xsize, //initial size + INT32 ysize, //in pixels + INT32 buflines //default scroll size + ) { + #ifdef __UNIX__ + INT32 length; /*length of name */ + char command[MAX_PATH]; /*pipe command */ + pid_t pid; /*process id */ + char host[MAX_PATH]; //remote host + BOOL8 remote; //remote host + + // remote=remote_display(host); //check remote host + remote = FALSE; + if (remote) + //do it remotely + length = sprintf (command, "remsh %s 'DISPLAY=%s;export DISPLAY;", host, getenv ("DISPLAY")); + else + length = 0; + length += sprintf (command + length, "trap \"\" 1 2 3 13 15\n"); + length += + sprintf (command + length, + "/usr/bin/X11/xterm -sb -sl " INT32FORMAT " -geometry " + INT32FORMAT "x" INT32FORMAT "", buflines, xsize / 8, ysize / 16); + if (xpos >= 0) + command[length++] = '+'; + length += sprintf (command + length, INT32FORMAT, xpos); + if (ypos >= 0) + command[length++] = '+'; + length += + sprintf (command + length, + INT32FORMAT " -title \"%s\" -n \"%s\" -e /bin/sh -c ", ypos, + title, title); + pid = getpid (); /*random number */ + length += + sprintf (command + length, + "\"stty opost; tty >/tmp/debug%d; while [ -s /tmp/debug%d ]\ndo\nsleep 1\ndone\" &\n", + pid, pid); + length += + sprintf (command + length, "trap \"rm -f /tmp/debug%d; kill -9 $!\" 0\n", + pid); + length += sprintf (command + length, "trap \"exit\" 1 2 3 13 15\n"); + length += + sprintf (command + length, + "while [ ! -s /tmp/debug%d ]\ndo\nsleep 1\ndone\n", pid); + length += sprintf (command + length, "trap \"\" 1 2 3 13 15\n"); + length += sprintf (command + length, "ofile=`cat /tmp/debug%d`\n", pid); + length += + sprintf (command + length, "cat -u - >$ofile; rm /tmp/debug%d\n", pid); + if (remote) { + command[length++] = '\''; //terminate remsh + command[length] = '\0'; + } + fp = popen (command, "w"); /*create window */ + if (fp != NULL) { + /*set no buffering */ + if (setvbuf (fp, NULL, _IONBF, BUFSIZ)) { + pclose(fp); + fp = NULL; + } + } + #endif +} + + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Close the file and destroy the window. + **********************************************************************/ + +DEBUG_WIN::~DEBUG_WIN ( +//destructor +) { + #ifdef __UNIX__ + pclose(fp); + #endif +} + + +/********************************************************************** + * dprintf + * + * Print a message to the debug window. + * Like printf, this function can cope with messages which do not end + * in newline, but nothing is printed until the newline is received. + **********************************************************************/ + +void +DEBUG_WIN::dprintf ( //debug printf +const char *format, ... //special message +) { + va_list args; //variable args + + va_start(args, format); //variable list + #ifdef __UNIX__ + vfprintf(fp, format, args); //Format into msg + #else + //Format into msg + vfprintf(stderr, format, args); + #endif + va_end(args); +} + + +/********************************************************************** + * await_destruction + * + * Wait for the user to close the debug window. Then return. + **********************************************************************/ + +void DEBUG_WIN::await_destruction() { //wait for user to close + #ifdef __UNIX__ + signal(SIGPIPE, SIG_IGN); + while (!ferror (fp)) { + sleep (1); + fputc (0, fp); //send nulls until error + } + #endif +} +#endif //UNIX Implmentation + +#ifdef __MAC__ //NT implementation +#include +//#include "textwindow.h" +#include +#include "ipcbase.h" //must be last include + +// Until I can figure a way to do this without linking in PowerPlant, +// the debug window will just have empty functions so compilation can take place. + +/********************************************************************** + * DEBUG_WIN::SetCommander + * + * Mac-specific function to set the commander for the next debug window + **********************************************************************/ +void DEBUG_WIN::SetCommander(LCommander *pNew) { + pCommander = pNew; +} + + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Create a debug window with size according to the arguments. + * Create an hpterm window with a pipe connected to it. + **********************************************************************/ + +DEBUG_WIN::DEBUG_WIN( //constructor + const char *title, //of window + INT32 xpos, //initial position + INT32 ypos, //in pixels + INT32 xsize, //initial size + INT32 ysize, //in pixels + INT32 buflines //default scroll size + ) { + INT32 length; /*length of name */ + + // don't replace this DebugStr() with a call to DEBUG_WIN! + + //if (pCommander==NULL) DebugStr("\pDEBUG_WIN::DEBUG_WIN(), Commander not set"); + + // create the window + + //pWindow=LWindow::CreateWindow(2700,pCommander); +} + + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Close the file and destroy the window. + **********************************************************************/ + +DEBUG_WIN::~DEBUG_WIN ( +//destructor +) { +} + + +/********************************************************************** + * dprintf + * + * Print a message to the debug window. + * Like printf, this function can cope with messages which do not end + * in newline, but nothing is printed until the newline is received. + **********************************************************************/ + +void +DEBUG_WIN::dprintf ( //debug printf +const char *format, ... //special message +) { + #if 0 + LTextEdit *pTextEdit; + va_list args; //variable args + static char msg[1024]; + + INT32 i; + INT32 OriginalLength; + INT32 NewLength; + TEHandle hTextEdit; + char *pTempBuffer; + CharsHandle hChar; + char *pOriginalText; + INT32 StringLength; + + pTextEdit = (LTextEdit *) pWindow->FindPaneByID (text_FLOWED); + if (pTextEdit == NULL) + DebugStr ("\pwhoops"); + + // get a C String from the format and args passed in + + va_start(args, format); //variable list + vsprintf(msg, format, args); //Format into msg + va_end(args); + + StringLength = strlen (msg); + + // get the handle for the text + + hTextEdit = pTextEdit->GetMacTEH (); + if (hTextEdit == NULL) + DebugStr ("\pDEBUG_WIN,WriteCharsToConsole()"); + + // get a pointer to the characters and the length of the character stream + + hChar = TEGetText (hTextEdit); + if (hChar == NULL) + DebugStr ("\pDEBUG_WIN,WriteCharsToConsole()"); + + pOriginalText = *hChar; // get pointer to existing text + + // get the length of the original data + OriginalLength = (*hTextEdit)->teLength; + + // setup a temporary buffer for the new text + + NewLength = OriginalLength + StringLength; + + pTempBuffer = NewPtr (NewLength); + if (pTempBuffer == NULL) + DebugStr ("\pDEBUG_WIN,WriteCharsToConsole()"); + + // copy the original data into the new buffer + + for (i = 0; i < OriginalLength; i++) + pTempBuffer[i] = pOriginalText[i]; + + // append the new data onto the end of the original buffer + + for (i = 0; i < StringLength; i++) { + if (msg[i] == '\n') + pTempBuffer[i + OriginalLength] = '\r'; + else + pTempBuffer[i + OriginalLength] = msg[i]; + } + + // put the new text into the text edit item + + TESetText(pTempBuffer, NewLength, hTextEdit); + + // clean up + + DisposePtr(pTempBuffer); + #endif +} +#endif //Mac Implmentation + +#else // Non graphical debugger + +DEBUG_WIN::DEBUG_WIN( const char*, INT32, INT32, INT32, INT32, INT32 ) { +} + +DEBUG_WIN::~DEBUG_WIN () { +} + +void DEBUG_WIN::dprintf (const char *format, ...) { + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} + +void await_destruction() { +} + + +#endif + diff --git a/ccutil/debugwin.h b/ccutil/debugwin.h new file mode 100644 index 0000000000..6115c69a51 --- /dev/null +++ b/ccutil/debugwin.h @@ -0,0 +1,103 @@ +/********************************************************************** + * File: debugwin.h + * Description: Portable debug window class. + * Author: Ray Smith + * Created: Wed Feb 21 15:36:59 MST 1996 + * + * (C) Copyright 1996, Hewlett-Packard Co. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef DEBUGWIN_H +#define DEBUGWIN_H + +#include "host.h" +#include "varable.h" + +#ifdef __MAC__ +#include +#include +#endif + +//the following define the default position of a debug window +//if not specified at construction +#define DEBUG_WIN_XPOS 50 //default position +#define DEBUG_WIN_YPOS 30 //default position +#define DEBUG_WIN_XSIZE 700 //default size +#define DEBUG_WIN_YSIZE 300 //default size + +//number of lines in the scrollable area of the window +extern DLLSYM INT_VAR_H (debug_lines, 256, "Number of lines in debug window"); + +//the API for the debug window is simple, see below. +//Most of its behaviour is its UI. +//It has a scrollable text area (most of the window) +//It has a stop control. +//It has a clear button. +//A dprintf to the window causes the text to be sent to the +//text area. If the stop control is set, then the dprintf +//blocks (does not display anything or return) until the stop +//is released. +//In multi-threaded apps, other threads and the UI continue to +//function during the stop. Only the calling thread is blocked. +//Pressing the clear button erases all text from the window. +//As text is sent to the window, it scrolls up so that the most +//recent output is visible. If the user has scrolled back, this +//does not happen. If the user scrolls back to the bottom, then +//the scrolling turns back on. +//If the user destroys the window, it never comes back. + +class DLLSYM DEBUG_WIN +{ + public: + //the constructor creates the window, the destructor kills it + DEBUG_WIN ( //constructor + const char *title, //of window + INT32 xpos = DEBUG_WIN_XPOS,//initial position + INT32 ypos = DEBUG_WIN_YPOS,//in pixels + //initial size + INT32 xsize = DEBUG_WIN_XSIZE, + //in pixels + INT32 ysize = DEBUG_WIN_YSIZE, + //default scroll size (textlines) + INT32 buflines = debug_lines); + + ~DEBUG_WIN (); //destructor + + void dprintf ( //printf to window + const char *format, ...); //message + + void await_destruction(); //wait for user to close + + #ifdef __MAC__ + static void SetCommander(LCommander *pCommander); + #endif + + private: + + #ifdef __MSW32__ + HWND handle; //handle to window + char *shm_mem; //shared memory + char *msg_end; //current string + HANDLE shm_hand; //handle to it + HANDLE dbg_process; //handle to it + HANDLE dbg_thread; //handle to it + #endif + #ifdef __UNIX__ + FILE *fp; /*return file */ + #endif + + #ifdef __MAC__ + LWindow *pWindow; + #endif +}; +#endif diff --git a/ccutil/elst.cpp b/ccutil/elst.cpp new file mode 100644 index 0000000000..1d5233b4ad --- /dev/null +++ b/ccutil/elst.cpp @@ -0,0 +1,593 @@ +/********************************************************************** + * File: elst.c (Formerly elist.c) + * Description: Embedded list handling code which is not in the include file. + * Author: Phil Cheatle + * Created: Fri Jan 04 13:55:49 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include "elst.h" + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: ELIST_LINK + * ===================================== + **********************************************************************/ + +/*********************************************************************** + * ELIST_LINK::serialise_asc + * + * Generates an error as it should never be called. + **********************************************************************/ + +void ELIST_LINK::serialise_asc( //default serialise + FILE *f) { + SERIALISE_LINKS.error ("ELIST_LINK::serialise_asc", ABORT, + "Don't call this, override!"); +} + + +void ELIST_LINK::de_serialise_asc( //default de_serialise + FILE *f) { + SERIALISE_LINKS.error ("ELIST_LINK::de_serialise_asc", ABORT, + "Don't call this, override!"); +} + + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: ELIST + * ================================ + **********************************************************************/ + +/*********************************************************************** + * ELIST::internal_clear + * + * Used by the destructor and the "clear" member function of derived list + * classes to destroy all the elements on the list. + * The calling function passes a "zapper" function which can be called to + * delete each element of the list, regardless of its derived type. This + * technique permits a generic clear function to destroy elements of + * different derived types correctly, without requiring virtual functions and + * the consequential memory overhead. + **********************************************************************/ + +void +ELIST::internal_clear ( //destroy all links +void (*zapper) (ELIST_LINK *)) { + //ptr to zapper functn + ELIST_LINK *ptr; + ELIST_LINK *next; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::internal_clear", ABORT, NULL); + #endif + + if (!empty ()) { + ptr = last->next; //set to first + last->next = NULL; //break circle + last = NULL; //set list empty + while (ptr) { + next = ptr->next; + zapper(ptr); + ptr = next; + } + } +} + + +/*********************************************************************** + * ELIST::internal_deep_copy + * + * Used during explict deep copy of a list. The "copier" function passed + * allows each element to be correctly deep copied (assuming that each class + * in the inheritance hierarchy does properly deep copies its members). The + * function passing technique is as for "internal_clear". + **********************************************************************/ + +void + //ptr to copier functn +ELIST::internal_deep_copy (ELIST_LINK * (*copier) (ELIST_LINK *), +const ELIST * list) { //list being copied + ELIST_ITERATOR from_it ((ELIST *) list); + ELIST_ITERATOR to_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::internal_deep_copy", ABORT, NULL); + if (!list) + BAD_PARAMETER.error ("ELIST::internal_deep_copy", ABORT, + "source list is NULL"); + #endif + + for (from_it.mark_cycle_pt (); !from_it.cycled_list (); from_it.forward ()) + to_it.add_after_then_move (copier (from_it.data ())); +} + + +/*********************************************************************** + * ELIST::assign_to_sublist + * + * The list is set to a sublist of another list. "This" list must be empty + * before this function is invoked. The two iterators passed must refer to + * the same list, different from "this" one. The sublist removed is the + * inclusive list from start_it's current position to end_it's current + * position. If this range passes over the end of the source list then the + * source list has its end set to the previous element of start_it. The + * extracted sublist is unaffected by the end point of the source list, its + * end point is always the end_it position. + **********************************************************************/ + +void ELIST::assign_to_sublist( //to this list + ELIST_ITERATOR *start_it, //from list start + ELIST_ITERATOR *end_it) { //from list end + const ERRCODE LIST_NOT_EMPTY = + "Destination list must be empty before extracting a sublist"; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::assign_to_sublist", ABORT, NULL); + #endif + + if (!empty ()) + LIST_NOT_EMPTY.error ("ELIST.assign_to_sublist", ABORT, NULL); + + last = start_it->extract_sublist (end_it); +} + + +/*********************************************************************** + * ELIST::length + * + * Return count of elements on list + **********************************************************************/ + +INT32 ELIST::length() { //count elements + ELIST_ITERATOR it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::length", ABORT, NULL); + #endif + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + count++; + return count; +} + + +/*********************************************************************** + * ELIST::sort + * + * Sort elements on list + * NB If you dont like the const declarations in the comparator, coerce yours: + * ( int (*)(const void *, const void *) + **********************************************************************/ + +void +ELIST::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + ELIST_ITERATOR it(this); + INT32 count; + ELIST_LINK **base; //ptr array to sort + ELIST_LINK **current; + INT32 i; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::sort", ABORT, NULL); + #endif + + /* Allocate an array of pointers, one per list element */ + count = length (); + base = (ELIST_LINK **) malloc (count * sizeof (ELIST_LINK *)); + + /* Extract all elements, putting the pointers in the array */ + current = base; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + *current = it.extract (); + current++; + } + + /* Sort the pointer array */ + qsort ((char *) base, count, sizeof (*base), comparator); + + /* Rebuild the list from the sorted pointers */ + current = base; + for (i = 0; i < count; i++) { + it.add_to_end (*current); + current++; + } + free(base); +} + + +/*********************************************************************** + * ELIST::prep_serialise + * + * Replace the last member with a count of elements for serialisation. + * This is used on list objects which are members of objects being + * serialised. The containing object has been shallow copied and this member + * function is invoked on the COPY. + **********************************************************************/ + +void ELIST::prep_serialise() { + ELIST_ITERATOR this_it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::prep_serialise", ABORT, NULL); + #endif + + count = 0; + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + count++; + last = (ELIST_LINK *) count; +} + + +/*********************************************************************** + * ELIST::internal_dump + * + * Cause each element on the list to be serialised by walking the list and + * calling the element_serialiser function for each element. The + * element_serialiser simply does the appropriate coercion of the element to + * its real type and then invokes the elements serialise function + **********************************************************************/ + +void +ELIST::internal_dump (FILE * f, +void element_serialiser (FILE *, ELIST_LINK *)) { + ELIST_ITERATOR this_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::internal_dump", ABORT, NULL); + #endif + + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + element_serialiser (f, this_it.data ()); +} + + +/*********************************************************************** + * ELIST::internal_de_dump + * + * Cause each element on the list to be de_serialised by extracting the count + * of elements on the list, (held in the last member of the dumped version of + * the list object), and then de-serialising that number of list elements, + * adding each to the end of the reconstructed list. + **********************************************************************/ + +void +ELIST::internal_de_dump (FILE * f, +ELIST_LINK * element_de_serialiser (FILE *)) { + INT32 count = (ptrdiff_t) last; + ELIST_ITERATOR this_it; + ELIST_LINK *de_serialised_element; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::internal_de_dump", ABORT, NULL); + #endif + + last = NULL; + this_it.set_to_list (this); + for (; count > 0; count--) { + de_serialised_element = element_de_serialiser (f); + //ignore old ptr + de_serialised_element->next = NULL; + this_it.add_to_end (de_serialised_element); + } +} + + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: ELIST_ITERATOR + * ========================================= + **********************************************************************/ + +/*********************************************************************** + * ELIST_ITERATOR::forward + * + * Move the iterator to the next element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + +ELIST_LINK *ELIST_ITERATOR::forward() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::forward", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::forward", ABORT, NULL); + #endif + if (list->empty ()) + return NULL; + + if (current) { //not removed so + //set previous + prev = current; + started_cycling = TRUE; + } + else { + if (ex_current_was_cycle_pt) + cycle_pt = next; + } + current = next; + next = current->next; + + #ifdef _DEBUG + if (!current) + NULL_DATA.error ("ELIST_ITERATOR::forward", ABORT, NULL); + if (!next) + NULL_NEXT.error ("ELIST_ITERATOR::forward", ABORT, + "This is: %i Current is: %i", + (int) this, (int) current); + #endif + return current; +} + + +/*********************************************************************** + * ELIST_ITERATOR::data_relative + * + * Return the data pointer to the element "offset" elements from current. + * "offset" must not be less than -1. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + +ELIST_LINK *ELIST_ITERATOR::data_relative( //get data + or - ... + INT8 offset) { //offset from current + ELIST_LINK *ptr; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); + if (offset < -1) + BAD_PARAMETER.error ("ELIST_ITERATOR::data_relative", ABORT, + "offset < -l"); + #endif + + if (offset == -1) + ptr = prev; + else + for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next); + + #ifdef _DEBUG + if (!ptr) + NULL_DATA.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); + #endif + + return ptr; +} + + +/*********************************************************************** + * ELIST_ITERATOR::move_to_last() + * + * Move current so that it is set to the end of the list. + * Return data just in case anyone wants it. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + +ELIST_LINK *ELIST_ITERATOR::move_to_last() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::move_to_last", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::move_to_last", ABORT, NULL); + #endif + + while (current != list->last) + forward(); + + return current; +} + + +/*********************************************************************** + * ELIST_ITERATOR::exchange() + * + * Given another iterator, whose current element is a different element on + * the same list list OR an element of another list, exchange the two current + * elements. On return, each iterator points to the element which was the + * other iterators current on entry. + * (This function hasn't been in-lined because its a bit big!) + **********************************************************************/ + +void ELIST_ITERATOR::exchange( //positions of 2 links + ELIST_ITERATOR *other_it) { //other iterator + const ERRCODE DONT_EXCHANGE_DELETED = + "Can't exchange deleted elements of lists"; + + ELIST_LINK *old_current; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::exchange", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::exchange", ABORT, NULL); + if (!other_it) + BAD_PARAMETER.error ("ELIST_ITERATOR::exchange", ABORT, "other_it NULL"); + if (!(other_it->list)) + NO_LIST.error ("ELIST_ITERATOR::exchange", ABORT, "other_it"); + #endif + + /* Do nothing if either list is empty or if both iterators reference the same + link */ + + if ((list->empty ()) || + (other_it->list->empty ()) || (current == other_it->current)) + return; + + /* Error if either current element is deleted */ + + if (!current || !other_it->current) + DONT_EXCHANGE_DELETED.error ("ELIST_ITERATOR.exchange", ABORT, NULL); + + /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements + (other before this); non-doubleton adjacent elements (this before other); + non-adjacent elements. */ + + //adjacent links + if ((next == other_it->current) || + (other_it->next == current)) { + //doubleton list + if ((next == other_it->current) && + (other_it->next == current)) { + prev = next = current; + other_it->prev = other_it->next = other_it->current; + } + else { //non-doubleton with + //adjacent links + //other before this + if (other_it->next == current) { + other_it->prev->next = current; + other_it->current->next = next; + current->next = other_it->current; + other_it->next = other_it->current; + prev = current; + } + else { //this before other + prev->next = other_it->current; + current->next = other_it->next; + other_it->current->next = current; + next = current; + other_it->prev = other_it->current; + } + } + } + else { //no overlap + prev->next = other_it->current; + current->next = other_it->next; + other_it->prev->next = current; + other_it->current->next = next; + } + + /* update end of list pointer when necessary (remember that the 2 iterators + may iterate over different lists!) */ + + if (list->last == current) + list->last = other_it->current; + if (other_it->list->last == other_it->current) + other_it->list->last = current; + + if (current == cycle_pt) + cycle_pt = other_it->cycle_pt; + if (other_it->current == other_it->cycle_pt) + other_it->cycle_pt = cycle_pt; + + /* The actual exchange - in all cases*/ + + old_current = current; + current = other_it->current; + other_it->current = old_current; +} + + +/*********************************************************************** + * ELIST_ITERATOR::extract_sublist() + * + * This is a private member, used only by ELIST::assign_to_sublist. + * Given another iterator for the same list, extract the links from THIS to + * OTHER inclusive, link them into a new circular list, and return a + * pointer to the last element. + * (Can't inline this function because it contains a loop) + **********************************************************************/ + +ELIST_LINK *ELIST_ITERATOR::extract_sublist( //from this current + ELIST_ITERATOR *other_it) { //to other current + #ifdef _DEBUG + const ERRCODE BAD_EXTRACTION_PTS = + "Can't extract sublist from points on different lists"; + const ERRCODE DONT_EXTRACT_DELETED = + "Can't extract a sublist marked by deleted points"; + #endif + const ERRCODE BAD_SUBLIST = "Can't find sublist end point in original list"; + + ELIST_ITERATOR temp_it = *this; + ELIST_LINK *end_of_new_list; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::extract_sublist", ABORT, NULL); + if (!other_it) + BAD_PARAMETER.error ("ELIST_ITERATOR::extract_sublist", ABORT, + "other_it NULL"); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::extract_sublist", ABORT, NULL); + if (list != other_it->list) + BAD_EXTRACTION_PTS.error ("ELIST_ITERATOR.extract_sublist", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("ELIST_ITERATOR::extract_sublist", ABORT, NULL); + + if (!current || !other_it->current) + DONT_EXTRACT_DELETED.error ("ELIST_ITERATOR.extract_sublist", ABORT, + NULL); + #endif + + ex_current_was_last = other_it->ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; + other_it->ex_current_was_cycle_pt = FALSE; + + temp_it.mark_cycle_pt (); + do { //walk sublist + if (temp_it.cycled_list ()) //cant find end pt + BAD_SUBLIST.error ("ELIST_ITERATOR.extract_sublist", ABORT, NULL); + + if (temp_it.at_last ()) { + list->last = prev; + ex_current_was_last = other_it->ex_current_was_last = TRUE; + } + + if (temp_it.current == cycle_pt) + ex_current_was_cycle_pt = TRUE; + + if (temp_it.current == other_it->cycle_pt) + other_it->ex_current_was_cycle_pt = TRUE; + + temp_it.forward (); + } + while (temp_it.prev != other_it->current); + + //circularise sublist + other_it->current->next = current; + end_of_new_list = other_it->current; + + //sublist = whole list + if (prev == other_it->current) { + list->last = NULL; + prev = current = next = NULL; + other_it->prev = other_it->current = other_it->next = NULL; + } + else { + prev->next = other_it->next; + current = other_it->current = NULL; + next = other_it->next; + other_it->prev = prev; + } + return end_of_new_list; +} diff --git a/ccutil/elst.h b/ccutil/elst.h new file mode 100644 index 0000000000..6ee8f0407a --- /dev/null +++ b/ccutil/elst.h @@ -0,0 +1,1146 @@ +/********************************************************************** + * File: elst.h (Formerly elist.h) + * Description: Embedded list module include file. + * Author: Phil Cheatle + * Created: Mon Jan 07 08:35:34 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef ELST_H +#define ELST_H + +#include +#include "host.h" +#include "serialis.h" +#include "lsterr.h" + +class ELIST_ITERATOR; + +/********************************************************************** +This module implements list classes and iterators. +The following list types and iterators are provided: + + List type List Class Iterator Class Element Class + --------- ---------- -------------- ------------- + + Embedded list ELIST + ELIST_ITERATOR + ELIST_LINK + (Single linked) + + Embedded list ELIST2 + ELIST2_ITERATOR + ELIST2_LINK + (Double linked) + + Cons List CLIST + CLIST_ITERATOR + CLIST_LINK + (Single linked) + + Cons List CLIST2 + CLIST2_ITERATOR + CLIST2_LINK + (Double linked) + +An embedded list is where the list pointers are provided by a generic class. +Data types to be listed inherit from the generic class. Data is thus linked +in only ONE list at any one time. + +A cons list has a separate structure for a "cons cell". This contains the +list pointer(s) AND a pointer to the data structure held on the list. A +structure can be on many cons lists at the same time, and the structure does +not need to inherit from any generic class in order to be on the list. + +The implementation of lists is very careful about space and speed overheads. +This is why many embedded lists are provided. The same concerns mean that +in-line type coercion is done, rather than use virtual functions. This is +cumbersome in that each data type to be listed requires its own iterator and +list class - though macros can gererate these. It also prevents heterogenous +lists. +**********************************************************************/ + +/********************************************************************** + * CLASS - ELIST_LINK + * + * Generic link class for singly linked lists with embedded links + * + * Note: No destructor - elements are assumed to be destroyed EITHER after + * they have been extracted from a list OR by the ELIST destructor which + * walks the list. + **********************************************************************/ + +class DLLSYM ELIST_LINK +{ + friend class ELIST_ITERATOR; + friend class ELIST; + + ELIST_LINK *next; + + public: + ELIST_LINK() { + next = NULL; + } + //constructor + + ELIST_LINK( //copy constructor + const ELIST_LINK &) { //dont copy link + next = NULL; + } + + void operator= ( //dont copy links + const ELIST_LINK &) { + next = NULL; + } + + void serialise_asc( //serialise to ascii + FILE *f); + void de_serialise_asc( //de-serialise from ascii + FILE *f); + + /* NOTE that none of the serialise member functions are required for + ELIST_LINKS as they are never serialised. (We demand that the derived + class terminates recursion - just to make sure that it defines the member + functions anyway.) + */ +}; + +/********************************************************************** + * CLASS - ELIST + * + * Generic list class for singly linked lists with embedded links + **********************************************************************/ + +class DLLSYM ELIST +{ + friend class ELIST_ITERATOR; + + ELIST_LINK *last; //End of list + //(Points to head) + ELIST_LINK *First() { // return first + return last ? last->next : NULL; + } + + public: + ELIST() { //constructor + last = NULL; + } + + void internal_clear ( //destroy all links + //ptr to zapper functn + void (*zapper) (ELIST_LINK *)); + + BOOL8 empty() { //is list empty? + return !last; + } + + BOOL8 singleton() { + return last ? (last == last->next) : FALSE; + } + + void shallow_copy( //dangerous!! + ELIST *from_list) { //beware destructors!! + last = from_list->last; + } + + //ptr to copier functn + void internal_deep_copy (ELIST_LINK * (*copier) (ELIST_LINK *), + const ELIST * list); //list being copied + + void assign_to_sublist( //to this list + ELIST_ITERATOR *start_it, //from list start + ELIST_ITERATOR *end_it); //from list end + + INT32 length(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + + void internal_dump ( //serialise each elem + FILE * f, //to this file + void element_serialiser ( //using this function + FILE *, ELIST_LINK *)); + + void internal_de_dump ( //de_serial each elem + FILE * f, //from this file + //using this function + ELIST_LINK * element_de_serialiser ( + FILE *)); + + void prep_serialise(); //change last to count + + /* Note that dump() and de_dump() are not required as calls to dump/de_dump a + list class should be handled by a class derived from this. + + make_serialise is not required for a similar reason. + */ +}; + +/*********************************************************************** + * CLASS - ELIST_ITERATOR + * + * Generic iterator class for singly linked lists with embedded links + **********************************************************************/ + +class DLLSYM ELIST_ITERATOR +{ + friend void ELIST::assign_to_sublist(ELIST_ITERATOR *, ELIST_ITERATOR *); + + ELIST *list; //List being iterated + ELIST_LINK *prev; //prev element + ELIST_LINK *current; //current element + ELIST_LINK *next; //next element + BOOL8 ex_current_was_last; //current extracted + //was end of list + BOOL8 ex_current_was_cycle_pt; //current extracted + //was cycle point + ELIST_LINK *cycle_pt; //point we are cycling + //the list to. + BOOL8 started_cycling; //Have we moved off + //the start? + + ELIST_LINK *extract_sublist( //from this current... + ELIST_ITERATOR *other_it); //to other current + + public: + ELIST_ITERATOR() { //constructor + list = NULL; + } //unassigned list + + ELIST_ITERATOR( //constructor + ELIST *list_to_iterate); + + void set_to_list( //change list + ELIST *list_to_iterate); + + void add_after_then_move( //add after current & + ELIST_LINK *new_link); //move to new + + void add_after_stay_put( //add after current & + ELIST_LINK *new_link); //stay at current + + void add_before_then_move( //add before current & + ELIST_LINK *new_link); //move to new + + void add_before_stay_put( //add before current & + ELIST_LINK *new_link); //stay at current + + void add_list_after( //add a list & + ELIST *list_to_add); //stay at current + + void add_list_before( //add a list & + ELIST *list_to_add); //move to it 1st item + + ELIST_LINK *data() { //get current data + #ifdef _DEBUG + if (!list) + NO_LIST.error ("ELIST_ITERATOR::data", ABORT, NULL); + if (!current) + NULL_DATA.error ("ELIST_ITERATOR::data", ABORT, NULL); + #endif + return current; + } + + ELIST_LINK *data_relative( //get data + or - ... + INT8 offset); //offset from current + + ELIST_LINK *forward(); //move to next element + + ELIST_LINK *extract(); //remove from list + + ELIST_LINK *move_to_first(); //go to start of list + + ELIST_LINK *move_to_last(); //go to end of list + + void mark_cycle_pt(); //remember current + + BOOL8 empty() { //is list empty? + #ifdef _DEBUG + if (!list) + NO_LIST.error ("ELIST_ITERATOR::empty", ABORT, NULL); + #endif + return list->empty (); + } + + BOOL8 current_extracted() { //current extracted? + return !current; + } + + BOOL8 at_first(); //Current is first? + + BOOL8 at_last(); //Current is last? + + BOOL8 cycled_list(); //Completed a cycle? + + void add_to_end( //add at end & + ELIST_LINK *new_link); //dont move + + void exchange( //positions of 2 links + ELIST_ITERATOR *other_it); //other iterator + + INT32 length(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + +}; + +/*********************************************************************** + * ELIST_ITERATOR::set_to_list + * + * (Re-)initialise the iterator to point to the start of the list_to_iterate + * over. + **********************************************************************/ + +inline void ELIST_ITERATOR::set_to_list( //change list + ELIST *list_to_iterate) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::set_to_list", ABORT, NULL); + if (!list_to_iterate) + BAD_PARAMETER.error ("ELIST_ITERATOR::set_to_list", ABORT, + "list_to_iterate is NULL"); + #endif + + list = list_to_iterate; + prev = list->last; + current = list->First (); + next = current ? current->next : NULL; + cycle_pt = NULL; //await explicit set + started_cycling = FALSE; + ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; +} + + +/*********************************************************************** + * ELIST_ITERATOR::ELIST_ITERATOR + * + * CONSTRUCTOR - set iterator to specified list; + **********************************************************************/ + +inline ELIST_ITERATOR::ELIST_ITERATOR(ELIST *list_to_iterate) { + set_to_list(list_to_iterate); +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_after_then_move + * + * Add a new element to the list after the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_after_then_move( // element to add + ELIST_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_after_then_move", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::add_after_then_move", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST_ITERATOR::add_after_then_move", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST_ITERATOR::add_after_then_move", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + new_element->next = next; + + if (current) { //not extracted + current->next = new_element; + prev = current; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + prev->next = new_element; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_after_stay_put + * + * Add a new element to the list after the current element but do not move + * the iterator to the new element. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_after_stay_put( // element to add + ELIST_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_after_stay_put", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::add_after_stay_put", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST_ITERATOR::add_after_stay_put", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST_ITERATOR::add_after_stay_put", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = FALSE; + current = NULL; + } + else { + new_element->next = next; + + if (current) { //not extracted + current->next = new_element; + if (prev == current) + prev = new_element; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + ex_current_was_last = FALSE; + } + } + next = new_element; + } +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_before_then_move + * + * Add a new element to the list before the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_before_then_move( // element to add + ELIST_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_before_then_move", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::add_before_then_move", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST_ITERATOR::add_before_then_move", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST_ITERATOR::add_before_then_move", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + prev->next = new_element; + if (current) { //not extracted + new_element->next = current; + next = current; + } + else { //current extracted + new_element->next = next; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_before_stay_put + * + * Add a new element to the list before the current element but dont move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_before_stay_put( // element to add + ELIST_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_before_stay_put", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::add_before_stay_put", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST_ITERATOR::add_before_stay_put", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST_ITERATOR::add_before_stay_put", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = TRUE; + current = NULL; + } + else { + prev->next = new_element; + if (current) { //not extracted + new_element->next = current; + if (next == current) + next = new_element; + } + else { //current extracted + new_element->next = next; + if (ex_current_was_last) + list->last = new_element; + } + prev = new_element; + } +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_list_after + * + * Insert another list to this list after the current element but dont move the + * iterator. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_list_after(ELIST *list_to_add) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_list_after", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::add_list_after", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error ("ELIST_ITERATOR::add_list_after", ABORT, + "list_to_add is NULL"); + #endif + + if (!list_to_add->empty ()) { + if (list->empty ()) { + list->last = list_to_add->last; + prev = list->last; + next = list->First (); + ex_current_was_last = TRUE; + current = NULL; + } + else { + if (current) { //not extracted + current->next = list_to_add->First (); + if (current == list->last) + list->last = list_to_add->last; + list_to_add->last->next = next; + next = current->next; + } + else { //current extracted + prev->next = list_to_add->First (); + if (ex_current_was_last) { + list->last = list_to_add->last; + ex_current_was_last = FALSE; + } + list_to_add->last->next = next; + next = prev->next; + } + } + list_to_add->last = NULL; + } +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_list_before + * + * Insert another list to this list before the current element. Move the + * iterator to the start of the inserted elements + * iterator. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_list_before(ELIST *list_to_add) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_list_before", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::add_list_before", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error ("ELIST_ITERATOR::add_list_before", ABORT, + "list_to_add is NULL"); + #endif + + if (!list_to_add->empty ()) { + if (list->empty ()) { + list->last = list_to_add->last; + prev = list->last; + current = list->First (); + next = current->next; + ex_current_was_last = FALSE; + } + else { + prev->next = list_to_add->First (); + if (current) { //not extracted + list_to_add->last->next = current; + } + else { //current extracted + list_to_add->last->next = next; + if (ex_current_was_last) + list->last = list_to_add->last; + if (ex_current_was_cycle_pt) + cycle_pt = prev->next; + } + current = prev->next; + next = current->next; + } + list_to_add->last = NULL; + } +} + + +/*********************************************************************** + * ELIST_ITERATOR::extract + * + * Do extraction by removing current from the list, returning it to the + * caller, but NOT updating the iterator. (So that any calling loop can do + * this.) The iterator's current points to NULL. If the extracted element + * is to be deleted, this is the callers responsibility. + **********************************************************************/ + +inline ELIST_LINK *ELIST_ITERATOR::extract() { + ELIST_LINK *extracted_link; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::extract", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::extract", ABORT, NULL); + if (!current) //list empty or + //element extracted + NULL_CURRENT.error ("ELIST_ITERATOR::extract", + ABORT, NULL); + #endif + + if (list->singleton ()) //special case where + //we do need to + prev = next = list->last = NULL; + // change the iterator + else { + prev->next = next; //remove from list + + if (current == list->last) { + list->last = prev; + ex_current_was_last = TRUE; + } + else + ex_current_was_last = FALSE; + + ex_current_was_cycle_pt = (current == cycle_pt) ? TRUE : FALSE; + + } + extracted_link = current; + extracted_link->next = NULL; //for safety + current = NULL; + return extracted_link; +} + + +/*********************************************************************** + * ELIST_ITERATOR::move_to_first() + * + * Move current so that it is set to the start of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + +inline ELIST_LINK *ELIST_ITERATOR::move_to_first() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::move_to_first", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::move_to_first", ABORT, NULL); + #endif + + current = list->First (); + prev = list->last; + next = current ? current->next : NULL; + return current; +} + + +/*********************************************************************** + * ELIST_ITERATOR::mark_cycle_pt() + * + * Remember the current location so that we can tell whether we've returned + * to this point later. + * + * If the current point is deleted either now, or in the future, the cycle + * point will be set to the next item which is set to current. This could be + * by a forward, add_after_then_move or add_after_then_move. + **********************************************************************/ + +inline void ELIST_ITERATOR::mark_cycle_pt() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::mark_cycle_pt", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::mark_cycle_pt", ABORT, NULL); + #endif + + if (current) + cycle_pt = current; + else + ex_current_was_cycle_pt = TRUE; + started_cycling = FALSE; +} + + +/*********************************************************************** + * ELIST_ITERATOR::at_first() + * + * Are we at the start of the list? + * + **********************************************************************/ + +inline BOOL8 ELIST_ITERATOR::at_first() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::at_first", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::at_first", ABORT, NULL); + #endif + + //we're at a deleted + return ((list->empty ()) || (current == list->First ()) || ((current == NULL) && + (prev == list->last) && //NON-last pt between + !ex_current_was_last)); //first and last +} + + +/*********************************************************************** + * ELIST_ITERATOR::at_last() + * + * Are we at the end of the list? + * + **********************************************************************/ + +inline BOOL8 ELIST_ITERATOR::at_last() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::at_last", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::at_last", ABORT, NULL); + #endif + + //we're at a deleted + return ((list->empty ()) || (current == list->last) || ((current == NULL) && + (prev == list->last) && //last point between + ex_current_was_last)); //first and last +} + + +/*********************************************************************** + * ELIST_ITERATOR::cycled_list() + * + * Have we returned to the cycle_pt since it was set? + * + **********************************************************************/ + +inline BOOL8 ELIST_ITERATOR::cycled_list() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::cycled_list", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::cycled_list", ABORT, NULL); + #endif + + return ((list->empty ()) || ((current == cycle_pt) && started_cycling)); + +} + + +/*********************************************************************** + * ELIST_ITERATOR::length() + * + * Return the length of the list + * + **********************************************************************/ + +inline INT32 ELIST_ITERATOR::length() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::length", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::length", ABORT, NULL); + #endif + + return list->length (); +} + + +/*********************************************************************** + * ELIST_ITERATOR::sort() + * + * Sort the elements of the list, then reposition at the start. + * + **********************************************************************/ + +inline void +ELIST_ITERATOR::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::sort", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::sort", ABORT, NULL); + #endif + + list->sort (comparator); + move_to_first(); +} + + +/*********************************************************************** + * ELIST_ITERATOR::add_to_end + * + * Add a new element to the end of the list without moving the iterator. + * This is provided because a single linked list cannot move to the last as + * the iterator couldn't set its prev pointer. Adding to the end is + * essential for implementing + queues. +**********************************************************************/ + +inline void ELIST_ITERATOR::add_to_end( // element to add + ELIST_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_to_end", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST_ITERATOR::add_to_end", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST_ITERATOR::add_to_end", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST_ITERATOR::add_to_end", ABORT, NULL); + #endif + + if (this->at_last ()) { + this->add_after_stay_put (new_element); + } + else { + if (this->at_first ()) { + this->add_before_stay_put (new_element); + list->last = new_element; + } + else { //Iteratr is elsewhere + new_element->next = list->last->next; + list->last->next = new_element; + list->last = new_element; + } + } +} + + +/*********************************************************************** + ******************** MACROS ************************************** + ***********************************************************************/ + +/*********************************************************************** + QUOTE_IT MACRO DEFINITION + =========================== +Replace with "". may be an arbitrary number of tokens +***********************************************************************/ + +#define QUOTE_IT( parm ) #parm + +/*********************************************************************** + ELISTIZE( CLASSNAME ) MACROS + ============================ + +CLASSNAME is assumed to be the name of a class which has a baseclass of +ELIST_LINK. + +NOTE: Because we dont use virtual functions in the list code, the list code +will NOT work correctly for classes derived from this. + +The macros generate: + - An element deletion function: CLASSNAME##_zapper + - An element copier function: CLASSNAME##_copier + - An element serialiser function" CLASSNAME##_serialiser + - An element de-serialiser function" CLASSNAME##_de_serialiser + - An E_LIST subclass: CLASSNAME##_LIST + - An E_LIST_ITERATOR subclass: CLASSNAME##_IT + +NOTE: Generated names are DELIBERATELY designed to clash with those for +ELIST2IZE but NOT with those for CLISTIZE and CLIST2IZE + +Four macros are provided: ELISTIZE, ELISTIZE_S, ELISTIZEH and ELISTIZEH_S +The ...IZEH macros just define the class names for use in .h files +The ...IZE macros define the code use in .c files +The _S versions define lists which can be serialised. They assume that +the make_serialise() macro is used in the list element class derived from +ELIST_LINK to define serialise() and de_serialise() members for the list +elements. +***********************************************************************/ + +/*********************************************************************** + ELISTIZEH( CLASSNAME ) and ELISTIZEH_S( CLASSNAME ) MACROS + +These macros are constructed from 3 fragments ELISTIZEH_A, ELISTIZEH_B and +ELISTIZEH_C. ELISTIZEH is simply a concatenation of these parts. +ELISTIZEH_S has some additional bits thrown in the gaps. +***********************************************************************/ + +#define ELISTIZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ +ELIST_LINK* link); /*link to delete*/ \ + \ +extern DLLSYM ELIST_LINK* CLASSNAME##_copier( /*deep copy a link*/ \ +ELIST_LINK* old_element); /*source link */ + +#define ELISTIZEH_B( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASS - CLASSNAME##_LIST \ +* \ +* List class for class CLASSNAME \ +* \ +**********************************************************************/ \ + \ +class DLLSYM CLASSNAME##_LIST : public ELIST \ +{ \ +public: \ + CLASSNAME##_LIST():ELIST() {}\ + /* constructor */ \ + \ + CLASSNAME##_LIST( /* dont construct */ \ + const CLASSNAME##_LIST&) /*by initial assign*/\ + { DONT_CONSTRUCT_LIST_BY_COPY.error( QUOTE_IT( CLASSNAME##_LIST ), \ + ABORT, NULL ); } \ + \ +void clear() /* delete elements */\ + { ELIST::internal_clear( &CLASSNAME##_zapper ); } \ + \ + ~CLASSNAME##_LIST() /* destructor */ \ + { clear(); } \ + \ +void deep_copy( /* become a deep */ \ + const CLASSNAME##_LIST* list) /* copy of src list*/\ + { ELIST::internal_deep_copy( &CLASSNAME##_copier, list ); } \ + \ +void operator=( /* prevent assign */ \ + const CLASSNAME##_LIST&) \ + { DONT_ASSIGN_LISTS.error( QUOTE_IT( CLASSNAME##_LIST ), \ + ABORT, NULL ); } + +#define ELISTIZEH_C( CLASSNAME ) \ +}; \ + \ + \ + \ +/*********************************************************************** \ +* CLASS - CLASSNAME##_IT \ +* \ +* Iterator class for class CLASSNAME##_LIST \ +* \ +* Note: We don't need to coerce pointers to member functions input \ +* parameters as these are automatically converted to the type of the base \ +* type. ("A ptr to a class may be converted to a pointer to a public base \ +* class of that class") \ +**********************************************************************/ \ + \ +class DLLSYM CLASSNAME##_IT : public ELIST_ITERATOR \ +{ \ +public: \ + CLASSNAME##_IT():ELIST_ITERATOR(){} \ + \ + CLASSNAME##_IT( \ +CLASSNAME##_LIST* list):ELIST_ITERATOR(list){} \ + \ + CLASSNAME* data() \ + { return (CLASSNAME*) ELIST_ITERATOR::data(); } \ + \ + CLASSNAME* data_relative( \ + INT8 offset) \ + { return (CLASSNAME*) ELIST_ITERATOR::data_relative( offset ); } \ + \ + CLASSNAME* forward() \ + { return (CLASSNAME*) ELIST_ITERATOR::forward(); } \ + \ + CLASSNAME* extract() \ + { return (CLASSNAME*) ELIST_ITERATOR::extract(); } \ + \ + CLASSNAME* move_to_first() \ + { return (CLASSNAME*) ELIST_ITERATOR::move_to_first(); } \ + \ + CLASSNAME* move_to_last() \ + { return (CLASSNAME*) ELIST_ITERATOR::move_to_last(); } \ +}; + +#define ELISTIZEH( CLASSNAME ) \ + \ +ELISTIZEH_A( CLASSNAME ) \ + \ +ELISTIZEH_B( CLASSNAME ) \ + \ +ELISTIZEH_C( CLASSNAME ) + +#define ELISTIZEH_S( CLASSNAME ) \ + \ +ELISTIZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_serialiser( \ +FILE* f, \ +ELIST_LINK* element); \ + \ +extern DLLSYM ELIST_LINK* CLASSNAME##_de_serialiser( \ +FILE* f); \ + \ +ELISTIZEH_B( CLASSNAME ) \ + \ + void dump( /* dump to file */ \ + FILE* f) \ + { ELIST::internal_dump( f, &CLASSNAME##_serialiser );} \ + \ + void de_dump( /* get from file */ \ + FILE* f) \ + { ELIST::internal_de_dump( f, &CLASSNAME##_de_serialiser );} \ + \ + void serialise_asc( /*dump to ascii*/ \ + FILE* f); \ + void de_serialise_asc( /*de-dump from ascii*/\ + FILE* f); \ + \ +make_serialise( CLASSNAME##_LIST ) \ + \ +ELISTIZEH_C( CLASSNAME ) + +/*********************************************************************** + ELISTIZE( CLASSNAME ) and ELISTIZE_S( CLASSNAME ) MACROS +ELISTIZE_S is a simple extension to ELISTIZE +***********************************************************************/ + +#define ELISTIZE( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASSNAME##_zapper \ +* \ +* A function which can delete a CLASSNAME element. This is passed to the \ +* generic clear list member function so that when a list is cleared the \ +* elements on the list are properly destroyed from the base class, even \ +* though we dont use a virtual destructor function. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ +ELIST_LINK* link) /*link to delete*/ \ +{ \ +delete (CLASSNAME *) link; \ +} \ + \ +/*********************************************************************** \ +* CLASSNAME##_copier \ +* \ +* A function which can generate a new, deep copy of a CLASSNAME element. \ +* This is passed to the generic deep copy list member function so that when \ +* a list is copied the elements on the list are properly copied from the \ +* base class, even though we dont use a virtual function. \ +**********************************************************************/ \ + \ +DLLSYM ELIST_LINK* CLASSNAME##_copier( /*deep copy a link*/ \ +ELIST_LINK* old_element) /*source link*/ \ +{ \ + CLASSNAME* new_element; \ + \ +new_element = new CLASSNAME; \ +*new_element = *((CLASSNAME*) old_element); \ +return (ELIST_LINK*) new_element; \ +} + +#define ELISTIZE_S( CLASSNAME ) \ + \ +ELISTIZE( CLASSNAME ) \ + \ + void CLASSNAME##_LIST::serialise_asc( \ + /*dump to ascii*/ \ + FILE* f) \ + { \ + CLASSNAME##_IT it(this); \ + \ + serialise_INT32(f,length()); \ + for (it.mark_cycle_pt();!it.cycled_list();it.forward()) \ + it.data()->serialise_asc(f); /*serialise the list*/\ + } \ + \ + void CLASSNAME##_LIST::de_serialise_asc( \ + /*de-dump from ascii*/\ + FILE* f) \ + { \ + INT32 len; /*length to retrive*/\ + CLASSNAME##_IT it; \ + CLASSNAME* new_elt=NULL; /*list element*/ \ + \ + len=de_serialise_INT32(f); \ + it.set_to_list(this); \ + for (;len>0;len--) \ + { \ + new_elt=new CLASSNAME; \ + new_elt->de_serialise_asc(f); \ + it.add_to_end(new_elt); /*put on the list*/ \ + } \ + return; \ + } \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_serialiser \ +* \ +* A function which can serialise an element \ +* This is passed to the generic dump member function so that when a list is \ +* serialised the elements on the list are properly serialised. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_serialiser( \ +FILE* f, \ +ELIST_LINK* element) \ +{ \ +((CLASSNAME*) element)->serialise( f ); \ +} \ + \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_de_serialiser \ +* \ +* A function which can de-serialise an element \ +* This is passed to the generic de-dump member function so that when a list \ +* is de-serialised the elements on the list are properly de-serialised. \ +**********************************************************************/ \ + \ +DLLSYM ELIST_LINK* CLASSNAME##_de_serialiser( \ +FILE* f) \ +{ \ +return (ELIST_LINK*) CLASSNAME::de_serialise( f ); \ +} +#endif diff --git a/ccutil/elst2.cpp b/ccutil/elst2.cpp new file mode 100644 index 0000000000..75a60c46eb --- /dev/null +++ b/ccutil/elst2.cpp @@ -0,0 +1,602 @@ +/********************************************************************** + * File: elst2.c (Formerly elist2.c) + * Description: Doubly linked embedded list code not in the include file. + * Author: Phil Cheatle + * Created: Wed Jan 23 11:04:47 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include "host.h" +#include "elst2.h" + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: ELIST2 + * ================================= + **********************************************************************/ + +/*********************************************************************** + * ELIST2::internal_clear + * + * Used by the destructor and the "clear" member function of derived list + * classes to destroy all the elements on the list. + * The calling function passes a "zapper" function which can be called to + * delete each element of the list, regardless of its derived type. This + * technique permits a generic clear function to destroy elements of + * different derived types correctly, without requiring virtual functions and + * the consequential memory overhead. + **********************************************************************/ + +void +ELIST2::internal_clear ( //destroy all links +void (*zapper) (ELIST2_LINK *)) { + //ptr to zapper functn + ELIST2_LINK *ptr; + ELIST2_LINK *next; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::internal_clear", ABORT, NULL); + #endif + + if (!empty ()) { + ptr = last->next; //set to first + last->next = NULL; //break circle + last = NULL; //set list empty + while (ptr) { + next = ptr->next; + zapper(ptr); + ptr = next; + } + } +} + + +/*********************************************************************** + * ELIST2::internal_deep_copy + * + * Used during explict deep copy of a list. The "copier" function passed + * allows each element to be correctly deep copied (assuming that each class + * in the inheritance hierarchy does properly deep copies its members). The + * function passing technique is as for "internal_clear". + **********************************************************************/ + +void + //ptr to copier functn +ELIST2::internal_deep_copy (ELIST2_LINK * (*copier) (ELIST2_LINK *), +const ELIST2 * list) { //list being copied + ELIST2_ITERATOR from_it ((ELIST2 *) list); + ELIST2_ITERATOR to_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::internal_deep_copy", ABORT, NULL); + if (!list) + BAD_PARAMETER.error ("ELIST2::internal_deep_copy", ABORT, + "source list is NULL"); + #endif + + for (from_it.mark_cycle_pt (); !from_it.cycled_list (); from_it.forward ()) + to_it.add_after_then_move (copier (from_it.data ())); +} + + +/*********************************************************************** + * ELIST2::assign_to_sublist + * + * The list is set to a sublist of another list. "This" list must be empty + * before this function is invoked. The two iterators passed must refer to + * the same list, different from "this" one. The sublist removed is the + * inclusive list from start_it's current position to end_it's current + * position. If this range passes over the end of the source list then the + * source list has its end set to the previous element of start_it. The + * extracted sublist is unaffected by the end point of the source list, its + * end point is always the end_it position. + **********************************************************************/ + +void ELIST2::assign_to_sublist( //to this list + ELIST2_ITERATOR *start_it, //from list start + ELIST2_ITERATOR *end_it) { //from list end + const ERRCODE LIST_NOT_EMPTY = + "Destination list must be empty before extracting a sublist"; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::assign_to_sublist", ABORT, NULL); + #endif + + if (!empty ()) + LIST_NOT_EMPTY.error ("ELIST2.assign_to_sublist", ABORT, NULL); + + last = start_it->extract_sublist (end_it); +} + + +/*********************************************************************** + * ELIST2::length + * + * Return count of elements on list + **********************************************************************/ + +INT32 ELIST2::length() { //count elements + ELIST2_ITERATOR it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::length", ABORT, NULL); + #endif + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + count++; + return count; +} + + +/*********************************************************************** + * ELIST2::sort + * + * Sort elements on list + * NB If you dont like the const declarations in the comparator, coerce yours: + * ( int (*)(const void *, const void *) + **********************************************************************/ + +void +ELIST2::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + ELIST2_ITERATOR it(this); + INT32 count; + ELIST2_LINK **base; //ptr array to sort + ELIST2_LINK **current; + INT32 i; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::sort", ABORT, NULL); + #endif + + /* Allocate an array of pointers, one per list element */ + count = length (); + base = (ELIST2_LINK **) malloc (count * sizeof (ELIST2_LINK *)); + + /* Extract all elements, putting the pointers in the array */ + current = base; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + *current = it.extract (); + current++; + } + + /* Sort the pointer array */ + qsort ((char *) base, count, sizeof (*base), comparator); + + /* Rebuild the list from the sorted pointers */ + current = base; + for (i = 0; i < count; i++) { + it.add_to_end (*current); + current++; + } + free(base); +} + + +/*********************************************************************** + * ELIST2::prep_serialise + * + * Replace the last member with a count of elements for serialisation. + * This is used on list objects which are members of objects being + * serialised. The containing object has been shallow copied and this member + * function is invoked on the COPY. + **********************************************************************/ + +void ELIST2::prep_serialise() { + ELIST2_ITERATOR this_it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::prep_serialise", ABORT, NULL); + #endif + + count = 0; + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + count++; + last = (ELIST2_LINK *) count; +} + + +/*********************************************************************** + * ELIST2::internal_dump + * + * Cause each element on the list to be serialised by walking the list and + * calling the element_serialiser function for each element. The + * element_serialiser simply does the appropriate coercion of the element to + * its real type and then invokes the elements serialise function + **********************************************************************/ + +void +ELIST2::internal_dump (FILE * f, +void element_serialiser (FILE *, ELIST2_LINK *)) { + ELIST2_ITERATOR this_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::internal_dump", ABORT, NULL); + #endif + + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + element_serialiser (f, this_it.data ()); +} + + +/*********************************************************************** + * ELIST2::internal_de_dump + * + * Cause each element on the list to be de_serialised by extracting the count + * of elements on the list, (held in the last member of the dumped version of + * the list object), and then de-serialising that number of list elements, + * adding each to the end of the reconstructed list. + **********************************************************************/ + +void +ELIST2::internal_de_dump (FILE * f, +ELIST2_LINK * element_de_serialiser (FILE *)) { + INT32 count = (ptrdiff_t) last; + ELIST2_ITERATOR this_it; + ELIST2_LINK *de_serialised_element; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::internal_de_dump", ABORT, NULL); + #endif + + last = NULL; + this_it.set_to_list (this); + for (; count > 0; count--) { + de_serialised_element = element_de_serialiser (f); + //ignore old ptr + de_serialised_element->next = NULL; + //ignore old ptr + de_serialised_element->prev = NULL; + this_it.add_to_end (de_serialised_element); + } +} + + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: ELIST2_ITERATOR + * ========================================== + **********************************************************************/ + +/*********************************************************************** + * ELIST2_ITERATOR::forward + * + * Move the iterator to the next element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + +ELIST2_LINK *ELIST2_ITERATOR::forward() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::forward", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::forward", ABORT, NULL); + #endif + if (list->empty ()) + return NULL; + + if (current) { //not removed so + //set previous + prev = current; + started_cycling = TRUE; + } + else { + if (ex_current_was_cycle_pt) + cycle_pt = next; + } + current = next; + next = current->next; + + #ifdef _DEBUG + if (!current) + NULL_DATA.error ("ELIST2_ITERATOR::forward", ABORT, NULL); + if (!next) + NULL_NEXT.error ("ELIST2_ITERATOR::forward", ABORT, + "This is: %i Current is: %i", + (int) this, (int) current); + #endif + return current; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::backward + * + * Move the iterator to the previous element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + +ELIST2_LINK *ELIST2_ITERATOR::backward() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::backward", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::backward", ABORT, NULL); + #endif + if (list->empty ()) + return NULL; + + if (current) { //not removed so + //set previous + next = current; + started_cycling = TRUE; + } + else { + if (ex_current_was_cycle_pt) + cycle_pt = prev; + } + current = prev; + prev = current->prev; + + #ifdef _DEBUG + if (!current) + NULL_DATA.error ("ELIST2_ITERATOR::backward", ABORT, NULL); + if (!prev) + NULL_PREV.error ("ELIST2_ITERATOR::backward", ABORT, + "This is: %i Current is: %i", + (int) this, (int) current); + #endif + return current; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::data_relative + * + * Return the data pointer to the element "offset" elements from current. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + +ELIST2_LINK *ELIST2_ITERATOR::data_relative( //get data + or - .. + INT8 offset) { //offset from current + ELIST2_LINK *ptr; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); + #endif + + if (offset < 0) + for (ptr = current ? current : next; offset++ < 0; ptr = ptr->prev); + else + for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next); + + #ifdef _DEBUG + if (!ptr) + NULL_DATA.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); + #endif + + return ptr; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::exchange() + * + * Given another iterator, whose current element is a different element on + * the same list list OR an element of another list, exchange the two current + * elements. On return, each iterator points to the element which was the + * other iterators current on entry. + * (This function hasn't been in-lined because its a bit big!) + **********************************************************************/ + +void ELIST2_ITERATOR::exchange( //positions of 2 links + ELIST2_ITERATOR *other_it) { //other iterator + const ERRCODE DONT_EXCHANGE_DELETED = + "Can't exchange deleted elements of lists"; + + ELIST2_LINK *old_current; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::exchange", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::exchange", ABORT, NULL); + if (!other_it) + BAD_PARAMETER.error ("ELIST2_ITERATOR::exchange", ABORT, "other_it NULL"); + if (!(other_it->list)) + NO_LIST.error ("ELIST2_ITERATOR::exchange", ABORT, "other_it"); + #endif + + /* Do nothing if either list is empty or if both iterators reference the same + link */ + + if ((list->empty ()) || + (other_it->list->empty ()) || (current == other_it->current)) + return; + + /* Error if either current element is deleted */ + + if (!current || !other_it->current) + DONT_EXCHANGE_DELETED.error ("ELIST2_ITERATOR.exchange", ABORT, NULL); + + /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements + (other before this); non-doubleton adjacent elements (this before other); + non-adjacent elements. */ + + //adjacent links + if ((next == other_it->current) || + (other_it->next == current)) { + //doubleton list + if ((next == other_it->current) && + (other_it->next == current)) { + prev = next = current; + other_it->prev = other_it->next = other_it->current; + } + else { //non-doubleton with + //adjacent links + //other before this + if (other_it->next == current) { + other_it->prev->next = current; + other_it->current->next = next; + other_it->current->prev = current; + current->next = other_it->current; + current->prev = other_it->prev; + next->prev = other_it->current; + + other_it->next = other_it->current; + prev = current; + } + else { //this before other + prev->next = other_it->current; + current->next = other_it->next; + current->prev = other_it->current; + other_it->current->next = current; + other_it->current->prev = prev; + other_it->next->prev = current; + + next = current; + other_it->prev = other_it->current; + } + } + } + else { //no overlap + prev->next = other_it->current; + current->next = other_it->next; + current->prev = other_it->prev; + next->prev = other_it->current; + other_it->prev->next = current; + other_it->current->next = next; + other_it->current->prev = prev; + other_it->next->prev = current; + } + + /* update end of list pointer when necessary (remember that the 2 iterators + may iterate over different lists!) */ + + if (list->last == current) + list->last = other_it->current; + if (other_it->list->last == other_it->current) + other_it->list->last = current; + + if (current == cycle_pt) + cycle_pt = other_it->cycle_pt; + if (other_it->current == other_it->cycle_pt) + other_it->cycle_pt = cycle_pt; + + /* The actual exchange - in all cases*/ + + old_current = current; + current = other_it->current; + other_it->current = old_current; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::extract_sublist() + * + * This is a private member, used only by ELIST2::assign_to_sublist. + * Given another iterator for the same list, extract the links from THIS to + * OTHER inclusive, link them into a new circular list, and return a + * pointer to the last element. + * (Can't inline this function because it contains a loop) + **********************************************************************/ + +ELIST2_LINK *ELIST2_ITERATOR::extract_sublist( //from this current + ELIST2_ITERATOR *other_it) { //to other current + #ifdef _DEBUG + const ERRCODE BAD_EXTRACTION_PTS = + "Can't extract sublist from points on different lists"; + const ERRCODE DONT_EXTRACT_DELETED = + "Can't extract a sublist marked by deleted points"; + #endif + const ERRCODE BAD_SUBLIST = "Can't find sublist end point in original list"; + + ELIST2_ITERATOR temp_it = *this; + ELIST2_LINK *end_of_new_list; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::extract_sublist", ABORT, NULL); + if (!other_it) + BAD_PARAMETER.error ("ELIST2_ITERATOR::extract_sublist", ABORT, + "other_it NULL"); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::extract_sublist", ABORT, NULL); + if (list != other_it->list) + BAD_EXTRACTION_PTS.error ("ELIST2_ITERATOR.extract_sublist", ABORT, NULL); + if (list->empty ()) + EMPTY_LIST.error ("ELIST2_ITERATOR::extract_sublist", ABORT, NULL); + + if (!current || !other_it->current) + DONT_EXTRACT_DELETED.error ("ELIST2_ITERATOR.extract_sublist", ABORT, + NULL); + #endif + + ex_current_was_last = other_it->ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; + other_it->ex_current_was_cycle_pt = FALSE; + + temp_it.mark_cycle_pt (); + do { //walk sublist + if (temp_it.cycled_list ()) //cant find end pt + BAD_SUBLIST.error ("ELIST2_ITERATOR.extract_sublist", ABORT, NULL); + + if (temp_it.at_last ()) { + list->last = prev; + ex_current_was_last = other_it->ex_current_was_last = TRUE; + } + + if (temp_it.current == cycle_pt) + ex_current_was_cycle_pt = TRUE; + + if (temp_it.current == other_it->cycle_pt) + other_it->ex_current_was_cycle_pt = TRUE; + + temp_it.forward (); + } + //do INCLUSIVE list + while (temp_it.prev != other_it->current); + + //circularise sublist + other_it->current->next = current; + //circularise sublist + current->prev = other_it->current; + end_of_new_list = other_it->current; + + //sublist = whole list + if (prev == other_it->current) { + list->last = NULL; + prev = current = next = NULL; + other_it->prev = other_it->current = other_it->next = NULL; + } + else { + prev->next = other_it->next; + other_it->next->prev = prev; + + current = other_it->current = NULL; + next = other_it->next; + other_it->prev = prev; + } + return end_of_new_list; +} diff --git a/ccutil/elst2.h b/ccutil/elst2.h new file mode 100644 index 0000000000..65ec696158 --- /dev/null +++ b/ccutil/elst2.h @@ -0,0 +1,1137 @@ +/********************************************************************** + * File: elst2.h (Formerly elist2.h) + * Description: Double linked embedded list module include file. + * Author: Phil Cheatle + * Created: Wed Jan 23 11:04:47 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef ELST2_H +#define ELST2_H + +#include +#include "host.h" +#include "serialis.h" +#include "lsterr.h" + +class ELIST2_ITERATOR; + +/********************************************************************** +DESIGN NOTE +=========== + +It would probably be possible to implement the ELIST2 classes as derived +classes from ELIST. I haven't done this because: + +a) I think it would be harder to understand the code +(Though the problem with not inheriting is that changes to ELIST must be + reflected in ELIST2 and vice versa) + +b) Most of the code is inline so: +i) The duplication in source does not affect the run time code size - the + code is copied inline anyway! + + ii) The compiler should have a bit less work to do! +**********************************************************************/ + +/********************************************************************** + * CLASS - ELIST2_LINK + * + * Generic link class for doubly linked lists with embedded links + * + * Note: No destructor - elements are assumed to be destroyed EITHER after + * they have been extracted from a list OR by the ELIST2 destructor which + * walks the list. + **********************************************************************/ + +class DLLSYM ELIST2_LINK +{ + friend class ELIST2_ITERATOR; + friend class ELIST2; + + ELIST2_LINK *prev; + ELIST2_LINK *next; + + public: + ELIST2_LINK() { //constructor + prev = next = NULL; + } + + ELIST2_LINK( //copy constructor + const ELIST2_LINK &) { //dont copy link + prev = next = NULL; + } + + void operator= ( //dont copy links + const ELIST2_LINK &) { + prev = next = NULL; + } + + /* NOTE that none of the serialise member functions are required for + ELIST2_LINKs as they are never serialised. (We demand that the derived + class terminates recursion - just to make sure that it defines the member + functions anyway.) + */ +}; + +/********************************************************************** + * CLASS - ELIST2 + * + * Generic list class for doubly linked lists with embedded links + **********************************************************************/ + +class DLLSYM ELIST2 +{ + friend class ELIST2_ITERATOR; + + ELIST2_LINK *last; //End of list + //(Points to head) + ELIST2_LINK *First() { // return first + return last ? last->next : NULL; + } + + public: + ELIST2() { //constructor + last = NULL; + } + + void internal_clear ( //destroy all links + void (*zapper) (ELIST2_LINK *)); + //ptr to zapper functn + + BOOL8 empty() { //is list empty? + return !last; + } + + BOOL8 singleton() { + return last ? (last == last->next) : FALSE; + } + + void shallow_copy( //dangerous!! + ELIST2 *from_list) { //beware destructors!! + last = from_list->last; + } + + //ptr to copier functn + void internal_deep_copy (ELIST2_LINK * (*copier) (ELIST2_LINK *), + const ELIST2 * list); //list being copied + + void assign_to_sublist( //to this list + ELIST2_ITERATOR *start_it, //from list start + ELIST2_ITERATOR *end_it); //from list end + + INT32 length(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + + void internal_dump ( //serialise each elem + FILE * f, //to this file + void element_serialiser ( //using this function + FILE *, ELIST2_LINK *)); + + void internal_de_dump ( //de_serial each elem + FILE * f, //from this file + //using this function + ELIST2_LINK * element_de_serialiser ( + FILE *)); + + void prep_serialise(); //change last to count + + /* Note that dump() and de_dump() are not required as calls to dump/de_dump a + list class should be handled by a class derived from this. + + make_serialise is not required for a similar reason. + */ +}; + +/*********************************************************************** + * CLASS - ELIST2_ITERATOR + * + * Generic iterator class for doubly linked lists with embedded links + **********************************************************************/ + +class DLLSYM ELIST2_ITERATOR +{ + friend void ELIST2::assign_to_sublist(ELIST2_ITERATOR *, ELIST2_ITERATOR *); + + ELIST2 *list; //List being iterated + ELIST2_LINK *prev; //prev element + ELIST2_LINK *current; //current element + ELIST2_LINK *next; //next element + BOOL8 ex_current_was_last; //current extracted + //was end of list + BOOL8 ex_current_was_cycle_pt; //current extracted + //was cycle point + ELIST2_LINK *cycle_pt; //point we are cycling + //the list to. + BOOL8 started_cycling; //Have we moved off + //the start? + + ELIST2_LINK *extract_sublist( //from this current... + ELIST2_ITERATOR *other_it); //to other current + + public: + ELIST2_ITERATOR() { //constructor + list = NULL; + } //unassigned list + + ELIST2_ITERATOR( //constructor + ELIST2 *list_to_iterate); + + void set_to_list( //change list + ELIST2 *list_to_iterate); + + void add_after_then_move( //add after current & + ELIST2_LINK *new_link); //move to new + + void add_after_stay_put( //add after current & + ELIST2_LINK *new_link); //stay at current + + void add_before_then_move( //add before current & + ELIST2_LINK *new_link); //move to new + + void add_before_stay_put( //add before current & + ELIST2_LINK *new_link); //stay at current + + void add_list_after( //add a list & + ELIST2 *list_to_add); //stay at current + + void add_list_before( //add a list & + ELIST2 *list_to_add); //move to it 1st item + + ELIST2_LINK *data() { //get current data + #ifdef _DEBUG + if (!current) + NULL_DATA.error ("ELIST2_ITERATOR::data", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::data", ABORT, NULL); + #endif + return current; + } + + ELIST2_LINK *data_relative( //get data + or - ... + INT8 offset); //offset from current + + ELIST2_LINK *forward(); //move to next element + + ELIST2_LINK *backward(); //move to prev element + + ELIST2_LINK *extract(); //remove from list + + //go to start of list + ELIST2_LINK *move_to_first(); + + ELIST2_LINK *move_to_last(); //go to end of list + + void mark_cycle_pt(); //remember current + + BOOL8 empty() { //is list empty? + #ifdef _DEBUG + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::empty", ABORT, NULL); + #endif + return list->empty (); + } + + BOOL8 current_extracted() { //current extracted? + return !current; + } + + BOOL8 at_first(); //Current is first? + + BOOL8 at_last(); //Current is last? + + BOOL8 cycled_list(); //Completed a cycle? + + void add_to_end( //add at end & + ELIST2_LINK *new_link); //dont move + + void exchange( //positions of 2 links + ELIST2_ITERATOR *other_it); //other iterator + + INT32 length(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + +}; + +/*********************************************************************** + * ELIST2_ITERATOR::set_to_list + * + * (Re-)initialise the iterator to point to the start of the list_to_iterate + * over. + **********************************************************************/ + +inline void ELIST2_ITERATOR::set_to_list( //change list + ELIST2 *list_to_iterate) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::set_to_list", ABORT, NULL); + if (!list_to_iterate) + BAD_PARAMETER.error ("ELIST2_ITERATOR::set_to_list", ABORT, + "list_to_iterate is NULL"); + #endif + + list = list_to_iterate; + prev = list->last; + current = list->First (); + next = current ? current->next : NULL; + cycle_pt = NULL; //await explicit set + started_cycling = FALSE; + ex_current_was_last = FALSE; + ex_current_was_cycle_pt = FALSE; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::ELIST2_ITERATOR + * + * CONSTRUCTOR - set iterator to specified list; + **********************************************************************/ + +inline ELIST2_ITERATOR::ELIST2_ITERATOR(ELIST2 *list_to_iterate) { + set_to_list(list_to_iterate); +} + + +/*********************************************************************** + * ELIST2_ITERATOR::add_after_then_move + * + * Add a new element to the list after the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_after_then_move( // element to add + ELIST2_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + new_element->next = next; + next->prev = new_element; + + if (current) { //not extracted + new_element->prev = current; + current->next = new_element; + prev = current; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + new_element->prev = prev; + prev->next = new_element; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::add_after_stay_put + * + * Add a new element to the list after the current element but do not move + * the iterator to the new element. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_after_stay_put( // element to add + ELIST2_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = FALSE; + current = NULL; + } + else { + new_element->next = next; + next->prev = new_element; + + if (current) { //not extracted + new_element->prev = current; + current->next = new_element; + if (prev == current) + prev = new_element; + if (current == list->last) + list->last = new_element; + } + else { //current extracted + new_element->prev = prev; + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + ex_current_was_last = FALSE; + } + } + next = new_element; + } +} + + +/*********************************************************************** + * ELIST2_ITERATOR::add_before_then_move + * + * Add a new element to the list before the current element and move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_before_then_move( // element to add + ELIST2_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + } + else { + prev->next = new_element; + new_element->prev = prev; + + if (current) { //not extracted + new_element->next = current; + current->prev = new_element; + next = current; + } + else { //current extracted + new_element->next = next; + next->prev = new_element; + if (ex_current_was_last) + list->last = new_element; + if (ex_current_was_cycle_pt) + cycle_pt = new_element; + } + } + current = new_element; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::add_before_stay_put + * + * Add a new element to the list before the current element but dont move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_before_stay_put( // element to add + ELIST2_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, NULL); + #endif + + if (list->empty ()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = TRUE; + current = NULL; + } + else { + prev->next = new_element; + new_element->prev = prev; + + if (current) { //not extracted + new_element->next = current; + current->prev = new_element; + if (next == current) + next = new_element; + } + else { //current extracted + new_element->next = next; + next->prev = new_element; + if (ex_current_was_last) + list->last = new_element; + } + prev = new_element; + } +} + + +/*********************************************************************** + * ELIST2_ITERATOR::add_list_after + * + * Insert another list to this list after the current element but dont move the + * iterator. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_list_after(ELIST2 *list_to_add) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_list_after", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_list_after", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_list_after", ABORT, + "list_to_add is NULL"); + #endif + + if (!list_to_add->empty ()) { + if (list->empty ()) { + list->last = list_to_add->last; + prev = list->last; + next = list->First (); + ex_current_was_last = TRUE; + current = NULL; + } + else { + if (current) { //not extracted + current->next = list_to_add->First (); + current->next->prev = current; + if (current == list->last) + list->last = list_to_add->last; + list_to_add->last->next = next; + next->prev = list_to_add->last; + next = current->next; + } + else { //current extracted + prev->next = list_to_add->First (); + prev->next->prev = prev; + if (ex_current_was_last) { + list->last = list_to_add->last; + ex_current_was_last = FALSE; + } + list_to_add->last->next = next; + next->prev = list_to_add->last; + next = prev->next; + } + } + list_to_add->last = NULL; + } +} + + +/*********************************************************************** + * ELIST2_ITERATOR::add_list_before + * + * Insert another list to this list before the current element. Move the + * iterator to the start of the inserted elements + * iterator. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_list_before(ELIST2 *list_to_add) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_list_before", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_list_before", ABORT, NULL); + if (!list_to_add) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_list_before", ABORT, + "list_to_add is NULL"); + #endif + + if (!list_to_add->empty ()) { + if (list->empty ()) { + list->last = list_to_add->last; + prev = list->last; + current = list->First (); + next = current->next; + ex_current_was_last = FALSE; + } + else { + prev->next = list_to_add->First (); + prev->next->prev = prev; + + if (current) { //not extracted + list_to_add->last->next = current; + current->prev = list_to_add->last; + } + else { //current extracted + list_to_add->last->next = next; + next->prev = list_to_add->last; + if (ex_current_was_last) + list->last = list_to_add->last; + if (ex_current_was_cycle_pt) + cycle_pt = prev->next; + } + current = prev->next; + next = current->next; + } + list_to_add->last = NULL; + } +} + + +/*********************************************************************** + * ELIST2_ITERATOR::extract + * + * Do extraction by removing current from the list, returning it to the + * caller, but NOT updating the iterator. (So that any calling loop can do + * this.) The iterator's current points to NULL. If the extracted element + * is to be deleted, this is the callers responsibility. + **********************************************************************/ + +inline ELIST2_LINK *ELIST2_ITERATOR::extract() { + ELIST2_LINK *extracted_link; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::extract", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::extract", ABORT, NULL); + if (!current) //list empty or + //element extracted + NULL_CURRENT.error ("ELIST2_ITERATOR::extract", + ABORT, NULL); + #endif + + if (list->singleton ()) //special case where + //we do need to + prev = next = list->last = NULL; + //change the iterator + else { + prev->next = next; //remove from list + next->prev = prev; + + if (current == list->last) { + list->last = prev; + ex_current_was_last = TRUE; + } + else + ex_current_was_last = FALSE; + + ex_current_was_cycle_pt = (current == cycle_pt) ? TRUE : FALSE; + + } + extracted_link = current; + extracted_link->next = NULL; //for safety + extracted_link->prev = NULL; //for safety + current = NULL; + return extracted_link; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::move_to_first() + * + * Move current so that it is set to the start of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + +inline ELIST2_LINK *ELIST2_ITERATOR::move_to_first() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::move_to_first", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::move_to_first", ABORT, NULL); + #endif + + current = list->First (); + prev = list->last; + next = current ? current->next : NULL; + return current; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::move_to_last() + * + * Move current so that it is set to the end of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + +inline ELIST2_LINK *ELIST2_ITERATOR::move_to_last() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::move_to_last", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::move_to_last", ABORT, NULL); + #endif + + current = list->last; + prev = current ? current->prev : NULL; + next = current ? current->next : NULL; + return current; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::mark_cycle_pt() + * + * Remember the current location so that we can tell whether we've returned + * to this point later. + * + * If the current point is deleted either now, or in the future, the cycle + * point will be set to the next item which is set to current. This could be + * by a forward, add_after_then_move or add_after_then_move. + **********************************************************************/ + +inline void ELIST2_ITERATOR::mark_cycle_pt() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::mark_cycle_pt", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::mark_cycle_pt", ABORT, NULL); + #endif + + if (current) + cycle_pt = current; + else + ex_current_was_cycle_pt = TRUE; + started_cycling = FALSE; +} + + +/*********************************************************************** + * ELIST2_ITERATOR::at_first() + * + * Are we at the start of the list? + * + **********************************************************************/ + +inline BOOL8 ELIST2_ITERATOR::at_first() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::at_first", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::at_first", ABORT, NULL); + #endif + + //we're at a deleted + return ((list->empty ()) || (current == list->First ()) || ((current == NULL) && + (prev == list->last) && //NON-last pt between + !ex_current_was_last)); //first and last +} + + +/*********************************************************************** + * ELIST2_ITERATOR::at_last() + * + * Are we at the end of the list? + * + **********************************************************************/ + +inline BOOL8 ELIST2_ITERATOR::at_last() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::at_last", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::at_last", ABORT, NULL); + #endif + + //we're at a deleted + return ((list->empty ()) || (current == list->last) || ((current == NULL) && + (prev == list->last) && //last point between + ex_current_was_last)); //first and last +} + + +/*********************************************************************** + * ELIST2_ITERATOR::cycled_list() + * + * Have we returned to the cycle_pt since it was set? + * + **********************************************************************/ + +inline BOOL8 ELIST2_ITERATOR::cycled_list() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::cycled_list", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::cycled_list", ABORT, NULL); + #endif + + return ((list->empty ()) || ((current == cycle_pt) && started_cycling)); + +} + + +/*********************************************************************** + * ELIST2_ITERATOR::length() + * + * Return the length of the list + * + **********************************************************************/ + +inline INT32 ELIST2_ITERATOR::length() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::length", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::length", ABORT, NULL); + #endif + + return list->length (); +} + + +/*********************************************************************** + * ELIST2_ITERATOR::sort() + * + * Sort the elements of the list, then reposition at the start. + * + **********************************************************************/ + +inline void +ELIST2_ITERATOR::sort ( //sort elements +int comparator ( //comparison routine +const void *, const void *)) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::sort", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::sort", ABORT, NULL); + #endif + + list->sort (comparator); + move_to_first(); +} + + +/*********************************************************************** + * ELIST2_ITERATOR::add_to_end + * + * Add a new element to the end of the list without moving the iterator. + * This is provided because a single linked list cannot move to the last as + * the iterator couldn't set its prev pointer. Adding to the end is + * essential for implementing + queues. +**********************************************************************/ + +inline void ELIST2_ITERATOR::add_to_end( // element to add + ELIST2_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_to_end", ABORT, NULL); + if (!list) + NO_LIST.error ("ELIST2_ITERATOR::add_to_end", ABORT, NULL); + if (!new_element) + BAD_PARAMETER.error ("ELIST2_ITERATOR::add_to_end", ABORT, + "new_element is NULL"); + if (new_element->next) + STILL_LINKED.error ("ELIST2_ITERATOR::add_to_end", ABORT, NULL); + #endif + + if (this->at_last ()) { + this->add_after_stay_put (new_element); + } + else { + if (this->at_first ()) { + this->add_before_stay_put (new_element); + list->last = new_element; + } + else { //Iteratr is elsewhere + new_element->next = list->last->next; + new_element->prev = list->last; + list->last->next->prev = new_element; + list->last->next = new_element; + list->last = new_element; + } + } +} + + +/*********************************************************************** + QUOTE_IT MACRO DEFINITION + =========================== +Replace with "". may be an arbitrary number of tokens +***********************************************************************/ + +#define QUOTE_IT( parm ) #parm + +/*********************************************************************** + ELIST2IZE( CLASSNAME ) MACRO DEFINITION + ====================================== + +CLASSNAME is assumed to be the name of a class which has a baseclass of +ELIST2_LINK. + +NOTE: Because we dont use virtual functions in the list code, the list code +will NOT work correctly for classes derived from this. + +The macro generates: + - An element deletion function: CLASSNAME##_zapper + - An element copier function: + CLASSNAME##_copier + - An element serialiser function" CLASSNAME##_serialiser + - An element de-serialiser function" CLASSNAME##_de_serialiser + - An E_LIST2 subclass: CLASSNAME##_LIST + - An E_LIST2_ITERATOR subclass: + CLASSNAME##_IT + +NOTE: Generated names are DELIBERATELY designed to clash with those for +ELISTIZE but NOT with those for CLISTIZE and CLIST2IZE + +Four macros are provided: ELIST2IZE, ELIST2IZE_S, ELIST2IZEH and ELIST2IZEH_S +The ...IZEH macros just define the class names for use in .h files +The ...IZE macros define the code use in .c files +The _S versions define lists which can be serialised. They assume that +the make_serialise() macro is used in the list element class derived from +ELIST2_LINK to define serialise() and de_serialise() members for the list +elements. +***********************************************************************/ + +/*********************************************************************** + ELIST2IZEH( CLASSNAME ) and ELIST2IZEH_S( CLASSNAME ) MACROS + +These macros are constructed from 3 fragments ELIST2IZEH_A, ELIST2IZEH_B and +ELIST2IZEH_C. ELIST2IZEH is simply a concatenation of these parts. +ELIST2IZEH_S has some additional bits thrown in the gaps. +***********************************************************************/ + +#define ELIST2IZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ +ELIST2_LINK* link); /*link to delete*/ \ + \ +extern DLLSYM ELIST2_LINK* CLASSNAME##_copier( /*deep copy a link*/ \ +ELIST2_LINK* old_element); /*source link */ + +#define ELIST2IZEH_B( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASS - CLASSNAME##_LIST \ +* \ +* List class for class CLASSNAME \ +* \ +**********************************************************************/ \ + \ +class DLLSYM CLASSNAME##_LIST : public ELIST2 \ +{ \ +public: \ + CLASSNAME##_LIST():ELIST2() {} \ + /* constructor */ \ + \ + CLASSNAME##_LIST( /* dont construct */ \ + const CLASSNAME##_LIST&) /*by initial assign*/\ + { DONT_CONSTRUCT_LIST_BY_COPY.error( QUOTE_IT( CLASSNAME##_LIST ), \ + ABORT, NULL ); } \ + \ +void clear() /* delete elements */\ + { ELIST2::internal_clear( &CLASSNAME##_zapper ); } \ + \ + ~CLASSNAME##_LIST() /* destructor */ \ + { clear(); } \ + \ +void deep_copy( /* become a deep */ \ + const CLASSNAME##_LIST* list) /* copy of src list*/\ + { ELIST2::internal_deep_copy( &CLASSNAME##_copier, list ); } \ + \ +void operator=( /* prevent assign */ \ + const CLASSNAME##_LIST&) \ + { DONT_ASSIGN_LISTS.error( QUOTE_IT( CLASSNAME##_LIST ), \ + ABORT, NULL ); } + +#define ELIST2IZEH_C( CLASSNAME ) \ +}; \ + \ + \ + \ +/*********************************************************************** \ +* CLASS - CLASSNAME##_IT \ +* \ +* Iterator class for class CLASSNAME##_LIST \ +* \ +* Note: We don't need to coerce pointers to member functions input \ +* parameters as these are automatically converted to the type of the base \ +* type. ("A ptr to a class may be converted to a pointer to a public base \ +* class of that class") \ +**********************************************************************/ \ + \ +class DLLSYM CLASSNAME##_IT : public ELIST2_ITERATOR \ +{ \ +public: \ + CLASSNAME##_IT():ELIST2_ITERATOR(){} \ + \ + CLASSNAME##_IT( \ +CLASSNAME##_LIST* list):ELIST2_ITERATOR(list){} \ + \ + CLASSNAME* data() \ + { return (CLASSNAME*) ELIST2_ITERATOR::data(); } \ + \ + CLASSNAME* data_relative( \ + INT8 offset) \ + { return (CLASSNAME*) ELIST2_ITERATOR::data_relative( offset ); } \ + \ + CLASSNAME* forward() \ + { return (CLASSNAME*) ELIST2_ITERATOR::forward(); } \ + \ + CLASSNAME* backward() \ + { return (CLASSNAME*) ELIST2_ITERATOR::backward(); } \ + \ + CLASSNAME* extract() \ + { return (CLASSNAME*) ELIST2_ITERATOR::extract(); } \ + \ + CLASSNAME* move_to_first() \ + { return (CLASSNAME*) ELIST2_ITERATOR::move_to_first(); } \ + \ + CLASSNAME* move_to_last() \ + { return (CLASSNAME*) ELIST2_ITERATOR::move_to_last(); } \ +}; + +#define ELIST2IZEH( CLASSNAME ) \ + \ +ELIST2IZEH_A( CLASSNAME ) \ + \ +ELIST2IZEH_B( CLASSNAME ) \ + \ +ELIST2IZEH_C( CLASSNAME ) + +#define ELIST2IZEH_S( CLASSNAME ) \ + \ +ELIST2IZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_serialiser( \ +FILE* f, \ +ELIST2_LINK* element); \ + \ +extern DLLSYM ELIST2_LINK* CLASSNAME##_de_serialiser( \ +FILE* f); \ + \ +ELIST2IZEH_B( CLASSNAME ) \ + \ + void dump( /* dump to file */ \ + FILE* f) \ + { ELIST2::internal_dump( f, &CLASSNAME##_serialiser );} \ + \ + void de_dump( /* get from file */ \ + FILE* f) \ + { ELIST2::internal_de_dump( f, &CLASSNAME##_de_serialiser );} \ + \ +make_serialise( CLASSNAME##_LIST ) \ + \ +ELIST2IZEH_C( CLASSNAME ) + +/*********************************************************************** + ELIST2IZE( CLASSNAME ) and ELIST2IZE_S( CLASSNAME ) MACROS +ELIST2IZE_S is a simple extension to ELIST2IZE +***********************************************************************/ + +#define ELIST2IZE( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASSNAME##_zapper \ +* \ +* A function which can delete a CLASSNAME element. This is passed to the \ +* generic clear list member function so that when a list is cleared the \ +* elements on the list are properly destroyed from the base class, even \ +* though we dont use a virtual destructor function. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ +ELIST2_LINK* link) /*link to delete*/ \ +{ \ +delete (CLASSNAME *) link; \ +} \ + \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_copier \ +* \ +* A function which can generate a new, deep copy of a CLASSNAME element. \ +* This is passed to the generic deep copy list member function so that when \ +* a list is copied the elements on the list are properly copied from the \ +* base class, even though we dont use a virtual function. \ +**********************************************************************/ \ + \ +DLLSYM ELIST2_LINK* CLASSNAME##_copier( /*deep copy a link*/ \ +ELIST2_LINK* old_element) /*source link*/ \ +{ \ + CLASSNAME* new_element; \ + \ +new_element = new CLASSNAME; \ +*new_element = *((CLASSNAME*) old_element); \ +return (ELIST2_LINK*) new_element; \ +} + +#define ELIST2IZE_S( CLASSNAME ) \ + \ +ELIST2IZE( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASSNAME##_serialiser \ +* \ +* A function which can serialise an element \ +* This is passed to the generic dump member function so that when a list is \ +* serialised the elements on the list are properly serialised. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_serialiser( \ +FILE* f, \ +ELIST2_LINK* element) \ +{ \ +((CLASSNAME*) element)->serialise( f ); \ +} \ + \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_de_serialiser \ +* \ +* A function which can de-serialise an element \ +* This is passed to the generic de-dump member function so that when a list \ +* is de-serialised the elements on the list are properly de-serialised. \ +**********************************************************************/ \ + \ +DLLSYM ELIST2_LINK* CLASSNAME##_de_serialiser( \ +FILE* f) \ +{ \ +return (ELIST2_LINK*) CLASSNAME::de_serialise( f ); \ +} +#endif diff --git a/ccutil/errcode.cpp b/ccutil/errcode.cpp new file mode 100644 index 0000000000..ef78cef9db --- /dev/null +++ b/ccutil/errcode.cpp @@ -0,0 +1,103 @@ +/********************************************************************** + * File: errcode.c (Formerly error.c) + * Description: Generic error handler function + * Author: Ray Smith + * Created: Tue May 1 16:28:39 BST 1990 + * + * (C) Copyright 1989, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include +#include +#include +#ifdef __UNIX__ +#include +#endif +#include "tprintf.h" +//#include "ipeerr.h" +#include "errcode.h" + +const ERRCODE BADERRACTION = "Illegal error action"; +#define MAX_MSG 1024 +extern INT16 global_abort_code; + +/********************************************************************** + * error + * + * Print an error message and continue, exit or abort according to action. + * Makes use of error messages and numbers in a common place. + * + **********************************************************************/ +void +ERRCODE::error ( //handle error +const char *caller, //name of caller +INT8 action, //action to take +const char *format, ... //special message +) const +{ + va_list args; //variable args + char msg[MAX_MSG]; + char *msgptr = msg; + + if (caller != NULL) + //name of caller + msgptr += sprintf (msgptr, "%s:", caller); + //actual message + msgptr += sprintf (msgptr, "Error:%s", message); + if (format != NULL) { + msgptr += sprintf (msgptr, ":"); + va_start(args, format); //variable list + #ifdef __MSW32__ + //print remainder + msgptr += _vsnprintf (msgptr, MAX_MSG - 2 - (msgptr - msg), format, args); + msg[MAX_MSG - 2] = '\0'; //ensure termination + strcat (msg, "\n"); + #else + //print remainder + msgptr += vsprintf (msgptr, format, args); + //no specific + msgptr += sprintf (msgptr, "\n"); + #endif + va_end(args); + } + else + //no specific + msgptr += sprintf (msgptr, "\n"); + + tprintf(msg); + if ((strstr (message, "File") != NULL) || + (strstr (message, "file") != NULL)) + global_abort_code = FILE_ABORT; + else if ((strstr (message, "List") != NULL) || + (strstr (message, "list") != NULL)) + global_abort_code = LIST_ABORT; + else if ((strstr (message, "Memory") != NULL) || + (strstr (message, "memory") != NULL)) + global_abort_code = MEMORY_ABORT; + else + global_abort_code = NO_ABORT_CODE; + + switch (action) { + case DBG: + case LOG: + return; //report only + case EXIT: + err_exit(); + case ABORT: + abort(); + default: + BADERRACTION.error ("error", ABORT, NULL); + } +} diff --git a/ccutil/errcode.h b/ccutil/errcode.h new file mode 100644 index 0000000000..dbff2adcc9 --- /dev/null +++ b/ccutil/errcode.h @@ -0,0 +1,104 @@ +/********************************************************************** + * File: errcode.h (Formerly error.h) + * Description: Header file for generic error handler class + * Author: Ray Smith + * Created: Tue May 1 16:23:36 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef ERRCODE_H +#define ERRCODE_H + +#include "host.h" + +/*Control parameters for error()*/ +#define DBG -1 /*log without alert */ +#define LOG 0 /*alert user */ +#define EXIT 1 /*exit after erro */ +#define ABORT 2 /*abort after error */ + +/* Explicit Error Abort codes */ +#define NO_ABORT_CODE 0 +#define LIST_ABORT 1 +#define MEMORY_ABORT 2 +#define FILE_ABORT 3 + +/* Location of code at error codes Reserve 0..2 (status codes 0..23 for UNLV)*/ +#define LOC_UNUSED0 0 +#define LOC_UNUSED1 1 +#define LOC_UNUSED2 2 +#define LOC_INIT 3 +#define LOC_EDGE_PROG 4 +#define LOC_TEXT_ORD_ROWS 5 +#define LOC_TEXT_ORD_WORDS 6 +#define LOC_PASS1 7 +#define LOC_PASS2 8 +/* Reserve up to 8..13 for adding subloc 0/3 plus subsubloc 0/1/2 */ +#define LOC_FUZZY_SPACE 14 +/* Reserve up to 14..20 for adding subloc 0/3 plus subsubloc 0/1/2 */ +#define LOC_MM_ADAPT 21 +#define LOC_DOC_BLK_REJ 22 +#define LOC_WRITE_RESULTS 23 +#define LOC_ADAPTIVE 24 +/* DONT DEFINE ANY LOCATION > 31 !!! */ + +/* Sub locatation determines whether pass2 was in normal mode or fix xht mode*/ +#define SUBLOC_NORM 0 +#define SUBLOC_FIX_XHT 3 + +/* Sub Sub locatation determines whether match_word_pass2 was in Tess + matcher, NN matcher or somewhere else */ + +#define SUBSUBLOC_OTHER 0 +#define SUBSUBLOC_TESS 1 +#define SUBSUBLOC_NN 2 + +class DLLSYM ERRCODE //error handler class +{ + const char *message; //error message + public: + void error ( //error print function + const char *caller, //function location + INT8 action, //action to take + const char *format, ... //fprintf format + ) const; + ERRCODE(const char *string) { + message = string; + } //initialize with string +}; + +const ERRCODE ASSERT_FAILED = "Assert failed"; + +#define ASSERT_HOST(x) if (!(x)) \ +{ \ + ASSERT_FAILED.error(#x,LOG,"in file %s, line %d", \ + __FILE__,__LINE__); \ +} + +void signal_exit( // + int signal_code //Signal which + ); +extern "C" +{ + void err_exit(); + //The real signal + void signal_termination_handler(int sig); +}; + +void set_global_loc_code(int loc_code); + +void set_global_subloc_code(int loc_code); + +void set_global_subsubloc_code(int loc_code); +#endif diff --git a/ccutil/fileerr.h b/ccutil/fileerr.h new file mode 100644 index 0000000000..d3b6993d21 --- /dev/null +++ b/ccutil/fileerr.h @@ -0,0 +1,34 @@ +/********************************************************************** + * File: fileerr.h (Formerly filerr.h) + * Description: Errors for file utilities. + * Author: Ray Smith + * Created: Tue Aug 14 15:45:16 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef FILEERR_H +#define FILEERR_H + +#include "errcode.h" + +const ERRCODE CANTOPENFILE = "Can't open file"; +const ERRCODE CANTCREATEFILE = "Can't create file"; +const ERRCODE CANTMAKEPIPE = "Can't create pipe"; +const ERRCODE CANTCONNECTPIPE = "Can't reconnect pipes to stdin/stdout"; +const ERRCODE READFAILED = "Read of file failed"; +const ERRCODE WRITEFAILED = "Write of file failed"; +const ERRCODE SELECTFAILED = "Select failed"; + +const ERRCODE EXECFAILED = "Could not exec new process"; +#endif diff --git a/ccutil/getopt.cpp b/ccutil/getopt.cpp new file mode 100644 index 0000000000..ef28825f75 --- /dev/null +++ b/ccutil/getopt.cpp @@ -0,0 +1,62 @@ +/********************************************************************** + * File: getopt.c + * Description: Re-implementation of the unix code. + * Author: Ray Smith + * Created: Tue Nov 28 05:52:50 MST 1995 + * + * (C) Copyright 1995, Hewlett-Packard Co. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include +#include "getopt.h" +#include "notdll.h" //must be last include + +int optind; +char *optarg; + +/********************************************************************** + * getopt + * + * parse command line args. + **********************************************************************/ + +int +getopt ( //parse args +INT32 argc, //arg count +char *argv[], //args +const char *arglist //string of arg chars +) { + char *arg; //arg char + + if (optind == 0) + optind = 1; + if (optind < argc && argv[optind][0] == '-') { + arg = strchr (arglist, argv[optind][1]); + if (arg == NULL || *arg == ':') + return '?'; //dud option + optind++; + optarg = argv[optind]; + if (arg[1] == ':') { + if (argv[optind - 1][2] != '\0') + //immediately after + optarg = argv[optind - 1] + 2; + else + optind++; + } + return *arg; + } + else + return EOF; +} diff --git a/ccutil/getopt.h b/ccutil/getopt.h new file mode 100644 index 0000000000..b094654b6e --- /dev/null +++ b/ccutil/getopt.h @@ -0,0 +1,30 @@ +/********************************************************************** + * File: getopt.h + * Description: Re-implementation of the unix code. + * Author: Ray Smith + * Created: Tue Nov 28 05:52:50 MST 1995 + * + * (C) Copyright 1995, Hewlett-Packard Co. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "host.h" +#include "notdll.h" //must be last include + +extern int optind; +extern char *optarg; + +int getopt ( //parse args +INT32 argc, //arg count +char *argv[], //args +const char *arglist //string of arg chars +); diff --git a/ccutil/globaloc.cpp b/ccutil/globaloc.cpp new file mode 100644 index 0000000000..439f4ee3e5 --- /dev/null +++ b/ccutil/globaloc.cpp @@ -0,0 +1,113 @@ +/********************************************************************** + * File: errcode.c (Formerly error.c) + * Description: Generic error handler function + * Author: Ray Smith + * Created: Tue May 1 16:28:39 BST 1990 + * + * (C) Copyright 1989, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include "errcode.h" +#include "tprintf.h" + +INT16 global_loc_code = LOC_INIT;//location code +INT16 global_subloc_code = SUBLOC_NORM; + //pass2 subloc code +INT16 global_subsubloc_code = SUBSUBLOC_OTHER; + //location code +INT16 global_abort_code = NO_ABORT_CODE; + //Prog abort code + +void signal_exit( // + int signal_code //Signal which + ) { + int exit_status; + + if ((global_loc_code == LOC_PASS2) || (global_loc_code == LOC_FUZZY_SPACE)) + global_loc_code += global_subloc_code + global_subsubloc_code; + + if (signal_code < 0) { + exit_status = global_loc_code * 8 + global_abort_code * 2 + 1; + tprintf ("Signal_exit %d ABORT. LocCode: %d AbortCode: %d\n", + exit_status, global_loc_code, global_abort_code); + } + else { + exit_status = global_loc_code * 8 + signal_code * 2; + tprintf ("Signal_exit %d SIGNAL ABORT. LocCode: %d SignalCode: %d\n", + exit_status, global_loc_code, signal_code); + } + + exit(exit_status); +} + + +/************************************************************************* + * err_exit() + * All program exits should go through this point. It allows a meaningful status + * code to be generated for the real exit() call. The status code is made up + * as follows: + * Bit 0 : 1 = Program Abort 0 = System Abort + * Bits 1,2 : IF bit 0 = 1 THEN ERRCODE::abort_code + * ELSE 0 = Bus Err or Seg Vi + * 1 = Floating point exception + * 2 = TimeOut (Signal 15 from command timer) + * 3 = Any other signal + * Bits 3..7 : Location code NEVER 0 ! + *************************************************************************/ + +//extern "C" { + +void err_exit() { + signal_exit (-1); +} + + +void signal_termination_handler( //The real signal + int sig) { + tprintf ("Signal_termination_handler called with signal %d\n", sig); + switch (sig) { + case SIGABRT: + signal_exit (-1); //use abort code + // case SIGBUS: + case SIGSEGV: + signal_exit (0); + case SIGFPE: + signal_exit (1); //floating point + case SIGTERM: + signal_exit (2); //timeout by cmdtimer + default: + signal_exit (3); //Anything else + } +} + + +//}; //end extern "C" + +void set_global_loc_code(int loc_code) { + global_loc_code = loc_code; + +} + + +void set_global_subloc_code(int loc_code) { + global_subloc_code = loc_code; + +} + + +void set_global_subsubloc_code(int loc_code) { + global_subsubloc_code = loc_code; + +} diff --git a/ccutil/globaloc.h b/ccutil/globaloc.h new file mode 100644 index 0000000000..b5086a2eaf --- /dev/null +++ b/ccutil/globaloc.h @@ -0,0 +1,40 @@ +/********************************************************************** + * File: errcode.h (Formerly error.h) + * Description: Header file for generic error handler class + * Author: Ray Smith + * Created: Tue May 1 16:23:36 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef GLOBALOC_H +#define GLOBALOC_H + +#include "hosthplb.h" +#include "notdll.h" + +void signal_exit( // + int signal_code //Signal which + ); +//extern "C" { +void err_exit(); + //The real signal +void signal_termination_handler(int sig); +//}; + +void set_global_loc_code(int loc_code); + +void set_global_subloc_code(int loc_code); + +void set_global_subsubloc_code(int loc_code); +#endif diff --git a/ccutil/hashfn.cpp b/ccutil/hashfn.cpp new file mode 100644 index 0000000000..fb5acc5542 --- /dev/null +++ b/ccutil/hashfn.cpp @@ -0,0 +1,57 @@ +/********************************************************************** + * File: hashfn.c (Formerly hash.c) + * Description: Simple hash function. + * Author: Ray Smith + * Created: Thu Jan 16 11:47:59 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include "hashfn.h" + +/********************************************************************** + * hash + * + * Simple hash function working on power of 2 table sizes. + * Uses xor function. Needs linear rehash. + **********************************************************************/ + +INT32 hash( //hash function + INT32 bits, //bits in hash function + void *key, //key to hash + INT32 keysize //size of key + ) { + INT32 bitindex; //current bit count + UINT32 keybits; //bit buffer + UINT32 hcode; //current hash code + UINT32 mask; //bit mask + + mask = (1 << bits) - 1; + keysize *= 8; //in bits + bitindex = 0; + keybits = 0; + hcode = 0; + do { + while (keysize > 0 && bitindex <= 24) { + keybits |= *((UINT8 *) key) << bitindex; + key = (UINT8 *) key + 1; + bitindex += 8; + keysize -= 8; + } + hcode ^= keybits & mask; //key new key + keybits >>= bits; + } + while (keysize > 0); + return hcode; //initial hash +} diff --git a/ccutil/hashfn.h b/ccutil/hashfn.h new file mode 100644 index 0000000000..d1c5495bd9 --- /dev/null +++ b/ccutil/hashfn.h @@ -0,0 +1,30 @@ +/********************************************************************** + * File: hashfn.h (Formerly hash.h) + * Description: Simple hash function. + * Author: Ray Smith + * Created: Thu Jan 16 11:47:59 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef HASHFN_H +#define HASHFN_H + +#include "host.h" + +INT32 hash( //hash function + INT32 bits, //bits in hash function + void *key, //key to hash + INT32 keysize //size of key + ); +#endif diff --git a/ccutil/host.h b/ccutil/host.h new file mode 100644 index 0000000000..6b2a3c2c19 --- /dev/null +++ b/ccutil/host.h @@ -0,0 +1,286 @@ +/****************************************************************************** + ** Filename: Host.h + ** Purpose: This is the system independent typedefs and defines + ** Author: MN, JG, MD + ** Version: 5.4.1 + ** History: 11/7/94 MCD received the modification that Lennart made + ** to port to 32 bit world and modify this file so that it + ** will be shared between platform. + ** 11/9/94 MCD Make MSW32 subset of MSW. Now MSW means + ** MicroSoft Window and MSW32 means the 32 bit worlds + ** of MicroSoft Window. Therefore you want the environment + ** to be MicroSoft Window and in the 32 bit world - + ** __MSW__ and __MSW32__ must be uncommented out. + ** 11/30/94 MCD Incorporated comments received for more + ** readability and the missing typedef for FLOAT. + ** 12/1/94 MCD Added PFVOID typedef + ** 5/1/95 MCD. Made many changes based on the inputs. + ** Changes: + ** 1) Rearrange the #ifdef so that there're definitions for + ** particular platforms. + ** 2) Took out the #define for computer and environment + ** that developer can uncomment + ** 3) Added __OLDCODE__ where the defines will be + ** obsoleted in the next version and advise not to use. + ** 4) Added the definitions for the following: + ** FILE_HANDLE, MEMORY_HANDLE, BOOL8, + ** MAX_INT8, MAX_INT16, MAX_INT32, MAX_UINT8 + ** MAX_UINT16, MAX_UINT32, MAX_FLOAT32 + ** 06/19/96 MCD. Took out MAX_FLOAT32 + ** 07/15/96 MCD. Fixed the comments error + ** Add back BOOL8. + ** + ** (c) Copyright Hewlett-Packard Company, 1988-1996. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#ifndef __HOST__ +#define __HOST__ + +/* +** Include automatically generated configuration file if running autoconf +*/ +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#if defined(MOTOROLA_BYTE_ORDER) || defined(WORDS_BIGENDIAN) +#define __MOTO__ // Big-endian. +#endif +#endif + + +/****************************************************************************** + ** IMPORTANT!!! ** + ** ** + ** Defines either __MSW__, __MSW32__, __MAC__, __UNIX__, __OS2__, __PM__ to + ** use the specified definitions indicated below in the preprocessor settings. ** + ** ** + ** Also define either __FarProc__ or __FarData__ and __MOTO__ to use the + ** specified definitions indicated below in the preprocessor settings. ** + ** ** + ** If a preprocessor settings is not allow in the compiler that is being use, + ** then it is recommended that a "platform.h" is created with the definition + ** of the computer and/or operating system. + ******************************************************************************/ + +#include "platform.h" +/* __MSW32__ */ +#ifdef __MSW32__ +#include +#include // winbase.h contains windows.h + +#define DLLIMPORT __declspec( dllimport) +#define DLLEXPORT __declspec( dllexport) + +typedef HANDLE FILE_HANDLE; +typedef HANDLE MEMORY_HANDLE; + +#else +/********************************************************/ +/* __MSW__ */ +#ifdef __MSW__ +#include // provides standard definitions (like HANDLE) + +#define DLLIMPORT __import +#define DLLEXPORT __export +/*----------------------------*/ +/*----------------------------*/ +typedef HANDLE FILE_HANDLE; +typedef HANDLE MEMORY_HANDLE; +/*----------------------------*/ +#ifndef BOOLEAN +typedef UINT16 BOOLEAN; +#endif // BOOLEAN +#endif +#endif + +/********************************************************/ +/* __MAC__ */ +#ifdef __MAC__ +#include +/*----------------------------*/ +/*----------------------------*/ +#define DLLIMPORT +#define DLLEXPORT + +// definitions of handles to relocatable blocks +typedef Handle HANDLE; // a handle to a relocatable memory block + +typedef short FILE_HANDLE; +typedef Handle MEMORY_HANDLE; +/*----------------------------*/ +#ifndef BOOLEAN +#define BOOLEAN Boolean +#endif +#endif +/********************************************************/ +#if defined(__UNIX__) || defined( __DOS__ ) || defined(__OS2__) || defined(__PM__) +/*----------------------------*/ +/* FarProc and FarData */ +/*----------------------------*/ +#define DLLIMPORT +#define DLLEXPORT +typedef void *HANDLE; +typedef HANDLE FILE_HANDLE; +typedef HANDLE MEMORY_HANDLE; +/*----------------------------*/ +#ifndef BOOLEAN +typedef unsigned short BOOLEAN; +#endif // BOOLEAN +#endif +/***************************************************************************** + ** + ** Standard GHC Definitions + ** + *****************************************************************************/ + +#ifdef __MOTO__ +#define __NATIVE__ MOTO +#else +#define __NATIVE__ INTEL +#endif + +//typedef HANDLE FD* PHANDLE; + +// definitions of portable data types (numbers and characters) +#if (_MSC_VER < 1400) // For VC 8.0. +typedef SIGNED char INT8; +#endif +typedef unsigned char UINT8; +typedef short INT16; +typedef unsigned short UINT16; +#if (_MSC_VER < 1200) //%%% vkr for VC 6.0 +typedef int INT32; +typedef unsigned int UINT32; +#endif //%%% vkr for VC 6.0 +typedef float FLOAT32; +typedef double FLOAT64; +typedef unsigned char BOOL8; + +// definitions of pointers to portable data types +#if (_MSC_VER < 1400) // For VC 8.0. +typedef SIGNED char *PINT8; +#endif +typedef unsigned char *PUINT8; +typedef short *PINT16; +typedef unsigned short *PUINT16; +#if (_MSC_VER < 1200) //%%% vkr for VC 6.0 +typedef int *PINT32; +typedef unsigned int *PUINT32; +#endif //%%% vkr for VC 6.0 +typedef float *PFLOAT32; +typedef double *PFLOAT64; + +// these are pointers to constant values (not constant pointers) + +typedef const SIGNED char *PCINT8; +typedef const unsigned char *PCUINT8; +typedef const short *PCINT16; +typedef const unsigned short *PCUINT16; +typedef const int *PCINT32; +typedef const unsigned int *PCUINT32; +typedef const float *PCFLOAT32; +typedef const double *PCFLOAT64; + +typedef void *PVOID; + +#define INT32FORMAT "%d" + +#define MAX_INT8 0x7f +#define MAX_INT16 0x7fff +#define MAX_INT32 0x7fffffff +#define MAX_UINT8 0xff +#define MAX_UINT16 0xffff +#define MAX_UINT32 0xffffffff +#define MAX_FLOAT32 ((float)3.40282347e+38) + +#define MIN_INT8 0x80 +#define MIN_INT16 0x8000 +#define MIN_INT32 0x80000000 +#define MIN_UINT8 0x00 +#define MIN_UINT16 0x0000 +#define MIN_UINT32 0x00000000 +#define MIN_FLOAT32 ((float)1.17549435e-38) + +// Defines + +#ifndef OKAY +#define OKAY 0 +#endif + +#ifndef HPERR +#define HPERR -1 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL 0L +#endif +/****************************************************************************** + ** WARNING!! ** + ** Below are definition that will be obsoleted in the next version. Please ** + ** do not continue to use the definition under __OLDCODE__. ** + *****************************************************************************/ +#ifdef __OLDCODE__ +#ifdef __MSW32__ + +#ifdef ERROR // Use HPERR +#undef ERROR +#define ERROR -1 +#endif + +typedef double FLOATHP; +#else + +#ifdef __MSW__ +#ifdef ERROR // Use HPERR +#undef ERROR +#define ERROR -1 +#endif +typedef double FLOAT; +typedef double FLOATHP; +typedef FLOAT FD *PFLOAT; +#endif +#endif +#ifdef __MAC__ +typedef float FLOAT; +typedef float FLOATHP; +typedef FLOAT FD *PFLOAT; +#endif + +#ifdef __UNIX__ +typedef float FLOAT; +typedef float FLOATHP; +typedef FLOAT FD *PFLOAT; +#endif + +#ifdef __DOS__ +typedef float FLOAT; +typedef float FLOATHP; +typedef FLOAT FD *PFLOAT; +#endif + +// definitions of pointers to functions that take no parameters +// specific definitions should be provided for functions that take parameters + +typedef void (far * PFVOID) (); /* pointer to function */ +typedef INT16 (*PFINT16) (void); +typedef INT32 (*PFINT32) (void); + +typedef BOOLEAN *PBOOLEAN; // a pointer to a Boolean +#endif +#endif diff --git a/ccutil/hosthplb.h b/ccutil/hosthplb.h new file mode 100644 index 0000000000..d81d9bc503 --- /dev/null +++ b/ccutil/hosthplb.h @@ -0,0 +1 @@ +#include "host.h" diff --git a/ccutil/lsterr.h b/ccutil/lsterr.h new file mode 100644 index 0000000000..5be6688f61 --- /dev/null +++ b/ccutil/lsterr.h @@ -0,0 +1,43 @@ +/********************************************************************** + * File: lsterr.h (Formerly listerr.h) + * Description: Errors shared by list modules + * Author: Phil Cheatle + * Created: Wed Jan 23 09:10:35 GMT 1991 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "errcode.h" //must be last include + +#ifndef LSTERR_H +#define LSTERR_H + +const ERRCODE DONT_CONSTRUCT_LIST_BY_COPY = +"Can't create a list by assignment"; +const ERRCODE DONT_ASSIGN_LISTS = "Can't assign to lists"; +const ERRCODE SERIALISE_LINKS = "Attempted to (de)serialise a link element"; + +#ifdef _DEBUG + +const ERRCODE NO_LIST = "Iterator not set to a list"; +const ERRCODE NULL_OBJECT = "List found this = NULL!"; +const ERRCODE NULL_DATA = "List would have returned a NULL data pointer"; +const ERRCODE NULL_CURRENT = "List current position is NULL"; +const ERRCODE NULL_NEXT = "Next element on the list is NULL"; +const ERRCODE NULL_PREV = "Previous element on the list is NULL"; +const ERRCODE EMPTY_LIST = "List is empty"; +const ERRCODE BAD_PARAMETER = "List parameter error"; +const ERRCODE STILL_LINKED = +"Attemting to add an element with non NULL links, to a list"; +#endif +#endif diff --git a/ccutil/mainblk.cpp b/ccutil/mainblk.cpp new file mode 100644 index 0000000000..87186c7e6d --- /dev/null +++ b/ccutil/mainblk.cpp @@ -0,0 +1,117 @@ +/********************************************************************** + * File: mainblk.c (Formerly main.c) + * Description: Function to call from main() to setup. + * Author: Ray Smith + * Created: Tue Oct 22 11:09:40 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#define BLOB_MATCHING_ON + +#include "mfcpch.h" +#include "fileerr.h" +#ifdef __UNIX__ +#include +#include +#else +#include +#endif +#include +#include "basedir.h" +#include "mainblk.h" + +#define VARDIR "configs/" /*variables files */ +#define EXTERN + +EXTERN DLLSYM STRING datadir; //dir for data files + //name of image +EXTERN DLLSYM STRING imagebasename; +EXTERN BOOL_VAR (m_print_variables, FALSE, +"Print initial values of all variables"); +EXTERN STRING_VAR (m_data_sub_dir, "tessdata/", "Directory for data files"); +EXTERN INT_VAR (memgrab_size, 13000000, "Preallocation size for batch use"); +const ERRCODE NO_PATH = +"Warning:explicit path for executable will not be used for configs"; +static const ERRCODE USAGE = "Usage"; + +/********************************************************************** + * main_setup + * + * Main for mithras demo program. Read the arguments and set up globals. + **********************************************************************/ + +void main_setup( /*main demo program */ + const char *argv0, //program name + const char *basename, //name of image + int argc, /*argument count */ + const char *const *argv /*arguments */ + ) { + INT32 arg; /*argument */ + INT32 offset; //for flag + FILE *fp; /*variables file */ + char flag[2]; //+/- + STRING varfile; /*name of file */ + + imagebasename = basename; /*name of image */ + if (getpath (argv0, datadir) < 0) + #ifdef __UNIX__ + CANTOPENFILE.error ("main", ABORT, "%s to get path", argv[0]); + #else + NO_PATH.error ("main", DBG, NULL); + #endif + + for (arg = 0; arg < argc; arg++) { + if (argv[arg][0] == '+' || argv[arg][0] == '-') { + offset = 1; + flag[0] = argv[arg][0]; + } + else { + offset = 0; + } + flag[offset] = '\0'; + varfile = flag; + /*attempt open */ + fp = fopen (argv[arg] + offset, "r"); + if (fp != NULL) { + fclose(fp); /*was only to test */ + } + else { + varfile += datadir; + varfile += m_data_sub_dir; /*data directory */ + varfile += VARDIR; /*variables dir */ + } + /*actual name */ + varfile += argv[arg] + offset; + read_variables_file (varfile.string ()); + } + + if (m_print_variables) + print_variables(stdout); /*print them all */ + + datadir += m_data_sub_dir; /*data directory */ + + #ifdef __UNIX__ + if (memgrab_size > 0) { + void *membuf; //test virtual mem + //test memory + membuf = malloc (memgrab_size); + if (membuf == NULL) { + raise(SIGTTOU); //hangup for jobber + sleep (10); + } + else + free(membuf); + } + #endif +} diff --git a/ccutil/mainblk.h b/ccutil/mainblk.h new file mode 100644 index 0000000000..8c4fe11fd2 --- /dev/null +++ b/ccutil/mainblk.h @@ -0,0 +1,48 @@ +/********************************************************************** + * File: mainblk.h (Formerly main.h) + * Description: Function to call from main() to setup. + * Author: Ray Smith + * Created: Tue Oct 22 11:09:40 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef MAINBLK_H +#define MAINBLK_H + +#include "varable.h" +#include "notdll.h" + +//#ifndef BLOB_MATCHING_ON +//class BLOB; +//class ROW; +// float compare_blobs(BLOB*,ROW*,BLOB*,ROW*) +//{ +// return (float)MAX_INT32; +//} +//#endif + +extern DLLSYM STRING datadir; //dir for data files + //name of image +extern DLLSYM STRING imagebasename; +extern BOOL_VAR_H (m_print_variables, FALSE, +"Print initial values of all variables"); +extern STRING_VAR_H (m_data_sub_dir, "data/", "Directory for data files"); +extern INT_VAR_H (memgrab_size, 13000000, "Preallocation size for batch use"); +void main_setup( /*main demo program */ + const char *argv0, //program name + const char *basename, //name of image + int argc, /*argument count */ + const char *const *argv /*arguments */ + ); +#endif diff --git a/ccutil/memblk.cpp b/ccutil/memblk.cpp new file mode 100644 index 0000000000..12dfba0ea9 --- /dev/null +++ b/ccutil/memblk.cpp @@ -0,0 +1,1106 @@ +/********************************************************************** + * File: memblk.c (Formerly memblock.c) + * Description: Enhanced instrumented memory allocator implemented as a class. + * Author: Ray Smith + * Created: Tue Jan 21 17:13:39 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include +#include "stderr.h" +#include "memryerr.h" +#include "hashfn.h" +#include "tprintf.h" +#include "memry.h" +#include "memblk.h" +#ifdef __UNIX__ +#include +#endif + +class UWREC +{ + public: + unsigned cur_frsize; //frame size + unsigned cursp; //stack + unsigned currls; //pc space + unsigned currlo; //pc offset + unsigned curdp; //data pointer + unsigned toprp; //rp + unsigned topmrp; //mrp + unsigned topsr0; //sr0 + unsigned topsr4; //sr4 + unsigned r3; //gr3 + unsigned cur_r19; //gr19 +}; + +MEMUNION *free_block = NULL; //head of freelist + +#define EXTERN + +EXTERN MEM_ALLOCATOR big_mem; +EXTERN MEM_ALLOCATOR main_mem; + //heads of freelists +EXTERN MEMUNION *free_structs[MAX_STRUCTS]; + //number issued +EXTERN INT32 structs_in_use[MAX_STRUCTS]; + //number issued +EXTERN INT32 blocks_in_use[MAX_STRUCTS]; + //head of block lists +EXTERN MEMUNION *struct_blocks[MAX_STRUCTS]; +EXTERN const char *owner_names[MAX_STRUCTS][MAX_CLASSES]; +EXTERN INT32 owner_counts[MAX_STRUCTS][MAX_CLASSES]; + //no of names +EXTERN INT16 name_counts[MAX_STRUCTS]; +EXTERN INT32 free_struct_blocks; //no of free blocks + +EXTERN INT_VAR (mem_mallocdepth, 0, "Malloc stack depth to trace"); +EXTERN INT_VAR (mem_mallocbits, 8, "Log 2 of hash table size"); +EXTERN INT_VAR (mem_freedepth, 0, "Free stack dpeth to trace"); +EXTERN INT_VAR (mem_freebits, 8, "Log 2 of hash table size"); +EXTERN INT_VAR (mem_countbuckets, 16, "No of buckets for histogram"); +EXTERN INT_VAR (mem_checkfreq, 0, "Calls to alloc_mem between owner counts"); + +/********************************************************************** + * MEM_ALLOCATOR::MEM_ALLOCATOR + * + * Constructor for a memory allocator. + **********************************************************************/ + +void +MEM_ALLOCATOR::init ( //initialize +void *(*ext_malloc) (INT32), //external source +void (*ext_free) (void *), //external free +INT32 firstsize, //size of first block +INT32 lastsize, //size of last block +INT32 maxchunk //biggest request +) { + blockcount = 0; + malloc_serial = 0; + topblock = NULL; + currblock = NULL; + callers = NULL; + malloc = ext_malloc; + free = ext_free; + maxsize = lastsize; + biggestblock = maxchunk; + totalmem = 0; + memsize = firstsize; + malloc_div_ratio = 1; + malloc_minor_serial = 0; + malloc_auto_count = 0; + call_bits = 0; + entries = 0; +} + + +/********************************************************************** + * MEM_ALLOCATOR::hash_caller + * + * Generate a hash code for a caller, setup the tables if necessary. + **********************************************************************/ + +UINT16 MEM_ALLOCATOR::hash_caller( //get hash code + void *addr //return address + ) { + INT32 index; //index to table + INT32 initial_hash; //initial index + + if (callers == NULL) + init_callers(); //setup table + //get hash code + initial_hash = hash (call_bits, &addr, sizeof (addr)); + if (initial_hash == 0) + initial_hash = 1; + index = initial_hash; + if (callers[index].caller != NULL && callers[index].caller != addr) { + do { + index++; + if (index >= entries) + index = 1; + } + while (callers[index].caller != NULL + && callers[index].caller != addr && index != initial_hash); + if (index == initial_hash) + index = 0; + } + if (callers[index].caller == NULL) { + if (index != 0) + callers[index].caller = addr; + if (callers[index].free_list == NULL) + //setup free table + callers[index].init_freeers (); + } + return (UINT16) index; +} + + +/********************************************************************** + * MALLOC_CALL::count_freeer + * + * Generate a hash code for a freeer, setup the tables if necessary. + * Then count the call. + **********************************************************************/ + +void MALLOC_CALL::count_freeer( //count calls to free + void *addr //return address + ) { + INT32 entries; //entries in table + INT32 index; //index to table + INT32 initial_hash; //initial index + + if (free_list == NULL) + init_freeers(); //setup table + entries = 1 << free_bits; + //get hash code + initial_hash = hash (free_bits, &addr, sizeof (addr)); + if (initial_hash == 0) + initial_hash = 1; + index = initial_hash; + if (free_list[index].freeer != NULL && free_list[index].freeer != addr) { + do { + index++; + if (index >= entries) + index = 1; + } + while (free_list[index].freeer != NULL + && free_list[index].freeer != addr && index != initial_hash); + if (index == initial_hash) + index = 0; + } + if (free_list[index].freeer == NULL && index != 0) { + free_list[index].freeer = addr; + } + free_list[index].count++; //count them +} + + +/********************************************************************** + * MEM_ALLOCATOR::init_callers + * + * Initialize the callers hash table. + **********************************************************************/ + +void MEM_ALLOCATOR::init_callers() { //setup hash table + INT32 depth = mem_mallocdepth; + + mem_mallocdepth.set_value (0); //can't register it + call_bits = mem_mallocbits; + entries = 1 << call_bits; + //make an array + callers = new MALLOC_CALL[entries]; + mem_mallocdepth.set_value (depth); +} + + +/********************************************************************** + * MALLOC_CALL::init_freeers + * + * Initialize the freeers hash table. + **********************************************************************/ + +void MALLOC_CALL::init_freeers() { //setup hash table + INT32 entries; //entries in table + INT32 depth = mem_mallocdepth; + + mem_mallocdepth.set_value (0); //can't register it + free_bits = mem_freebits; + entries = 1 << free_bits; + //make an array + free_list = new FREE_CALL[entries]; + mem_mallocdepth.set_value (depth); +} + + +/********************************************************************** + * MEM_ALLOCATOR::reduce_counts + * + * Divide all ages by 2 to get a log use of counts. + **********************************************************************/ + +void MEM_ALLOCATOR::reduce_counts() { //divide by 2 + MEMBLOCK *block; //current block + MEMUNION *chunk; //current chunk + INT32 chunksize; //size of chunk + INT32 blockindex; //index of block + + check_mem ("Reducing counts", JUSTCHECKS); + for (blockindex = 0; blockindex < blockcount; blockindex++) { + //current block + block = &memblocks[blockindex]; + //scan all chunks + for (chunk = block->blockstart; chunk != block->blockend; chunk += chunksize) { + chunksize = chunk->size; //size of chunk + if (chunksize < 0) + chunksize = -chunksize; //absolute size + chunk->age /= 2; //divide ages + } + } +} + + +/********************************************************************** + * MEM_ALLOCATOR::display_counts + * + * Send counts of outstanding blocks to stderr. + **********************************************************************/ + +void MEM_ALLOCATOR::display_counts() { //count up + MEMBLOCK *block; //current block + MEMUNION *chunk; //current chunk + INT32 chunksize; //size of chunk + INT32 blockindex; //index of block + INT32 buckets; //required buckets + INT32 bucketsize; //no in each bucket + INT32 callindex; //index to callers + INT32 freeindex; //index to freeers + INT32 freeentries; //table size + INT32 totalchunks; //total chunk counts + INT32 totalspace; //total mem space + INT32 totalpchunks; //permanent chunks + INT32 totalpspace; //permanent space + INT32 totalfrees; //total free calls + + if (callers == NULL) + return; //can't do anything + check_mem ("Displaying counts", JUSTCHECKS); + buckets = mem_countbuckets; + bucketsize = (malloc_serial - 1) / buckets + 1; + tprintf ("\nEach bucket covers %g counts.\n", + (double) bucketsize * malloc_div_ratio); + for (callindex = 0; callindex < entries; callindex++) { + if (callers[callindex].free_list != NULL) { + callers[callindex].counts = + (INT32 *) malloc (buckets * 4 * sizeof (INT32)); + memset (callers[callindex].counts, 0, + (size_t) (buckets * 4 * sizeof (INT32))); + } + } + for (blockindex = 0; blockindex < blockcount; blockindex++) { + //current block + block = &memblocks[blockindex]; + //scan all chunks + for (chunk = block->blockstart; chunk != block->topchunk; chunk += chunksize) { + chunksize = chunk->size; //size of chunk + if (chunksize < 0) { + chunksize = -chunksize; //absolute size + callindex = chunk->owner; + if (callers[callindex].counts != NULL) { + callers[callindex].counts[chunk->age / bucketsize * 4]++; + callers[callindex].counts[chunk->age / bucketsize * 4 + + 1] += chunksize; + } + } + } + //scan all chunks + for (; chunk != block->blockend; chunk += chunksize) { + chunksize = chunk->size; //size of chunk + if (chunksize < 0) { + chunksize = -chunksize; //absolute size + callindex = chunk->owner; + if (callers[callindex].counts != NULL) { + callers[callindex].counts[chunk->age / bucketsize * 4 + + 2]++; + callers[callindex].counts[chunk->age / bucketsize * 4 + + 3] += chunksize; + } + } + } + } + for (callindex = 0; callindex < entries; callindex++) { + if (callers[callindex].counts != NULL) { + for (totalspace = 0, totalchunks = 0, totalpspace = + 0, totalpchunks = 0, freeindex = 0; freeindex < buckets; + freeindex++) { + totalchunks += callers[callindex].counts[freeindex * 4]; + totalspace += callers[callindex].counts[freeindex * 4 + 1]; + totalpchunks += callers[callindex].counts[freeindex * 4 + 2]; + totalpspace += callers[callindex].counts[freeindex * 4 + 3]; + } + freeentries = 1 << callers[callindex].free_bits; + for (totalfrees = 0, freeindex = 0; freeindex < freeentries; + freeindex++) + totalfrees += callers[callindex].free_list[freeindex].count; + if (totalspace != 0 || totalfrees != 0) { + tprintf ("alloc_mem at %d : total held=%d(%d), frees=%d.\n", + callers[callindex].caller, + totalchunks, totalspace * sizeof (MEMUNION), + totalfrees); + } + if (totalspace > 0) { + for (freeindex = 0; freeindex < buckets; freeindex++) { + tprintf ("%d(%d) ", + callers[callindex].counts[freeindex * 4], + callers[callindex].counts[freeindex * 4 + + 1] * sizeof (MEMUNION)); + } + tprintf ("\n"); + } + if (totalfrees != 0) { + tprintf ("Calls to free : "); + for (freeindex = 0; freeindex < freeentries; freeindex++) { + if (callers[callindex].free_list[freeindex].count != 0) + tprintf ("%d : %d ", + callers[callindex].free_list[freeindex].freeer, + callers[callindex].free_list[freeindex].count); + } + tprintf ("\n"); + } + if (totalpspace != 0) { + tprintf ("alloc_mem_p at %d : total held=%d(%d).\n", + callers[callindex].caller, + totalpchunks, totalpspace * sizeof (MEMUNION)); + for (freeindex = 0; freeindex < buckets; freeindex++) { + tprintf ("%d(%d) ", + callers[callindex].counts[freeindex * 4 + 2], + callers[callindex].counts[freeindex * 4 + + 3] * sizeof (MEMUNION)); + } + tprintf ("\n"); + } + free (callers[callindex].counts); + callers[callindex].counts = NULL; + } + } +} + + +/********************************************************************** + * MEM_ALLOCATOR::check + * + * Check consistency of all memory controlled by this allocator. + **********************************************************************/ + +void MEM_ALLOCATOR::check( //check consistency + const char *string, //context message + INT8 level //level of check + ) { + MEMBLOCK *block; //current block + MEMUNION *chunk; //current chunk + MEMUNION *prevchunk; //previous chunk + INT32 chunksize; //size of chunk + INT32 usedcount; //no of used chunks + INT32 usedsize; //size of used chunks + INT32 freecount; //no of free chunks + INT32 freesize; //size of free chunks + INT32 biggest; //biggest free chunk + INT32 totusedcount; //no of used chunks + INT32 totusedsize; //size of used chunks + INT32 totfreecount; //no of free chunks + INT32 totfreesize; //size of free chunks + INT32 totbiggest; //biggest free chunk + INT32 totblocksize; //total size of blocks + INT32 chunkindex; //index of chunk + INT32 blockindex; //index of block + + if (level >= MEMCHECKS) + tprintf ("\nMEM_ALLOCATOR::check:at '%s'\n", string); + totusedcount = 0; //grand totals + totusedsize = 0; + totfreecount = 0; + totfreesize = 0; + totbiggest = 0; + totblocksize = 0; + for (blockindex = 0; blockindex < blockcount; blockindex++) { + //current block + block = &memblocks[blockindex]; + if (level >= MEMCHECKS) + tprintf ("Block %d:0x%x-0x%x, size=%d, top=0x%x, l=%d, u=%d\n", + blockindex, block->blockstart, block->blockend, + (block->blockend - block->blockstart) * sizeof (MEMUNION), + block->topchunk, block->lowerspace, block->upperspace); + usedcount = usedsize = 0; //zero counters + freecount = freesize = 0; //zero counters + biggest = 0; + //scan all chunks + for (chunkindex = 0, prevchunk = NULL, chunk = block->blockstart; chunk != block->blockend; chunkindex++, chunk += chunksize) { + chunksize = chunk->size; //size of chunk + if (chunksize < 0) + chunksize = -chunksize; //absolute size + if (level >= FULLMEMCHECKS) { + tprintf ("%5d=%8d%c caller=%d, age=%d ", (int) chunkindex, + chunksize * sizeof (MEMUNION), + chunk->size < 0 ? 'U' : 'F', chunk->owner, chunk->age); + if (chunkindex % 5 == 4) + tprintf ("\n"); + } + //illegal sizes + if (chunksize == 0 || chunk->size == -1 + //out of bounds + || chunk + chunksize - block->blockstart <= 0 || block->blockend - (chunk + chunksize) < 0) + BADMEMCHUNKS.error ("check_mem", ABORT, + "Block=%p, Prev chunk=%p, Chunk=%p, Size=%x", + block, prevchunk, chunk, + (int) chunk->size); + + else if (chunk->size < 0) { + usedcount++; //used block + usedsize += chunksize; + } + else { + freecount++; //free block + freesize += chunksize; + if (chunksize > biggest) + biggest = chunksize; + } + prevchunk = chunk; + } + if (level >= MEMCHECKS) { + if (level >= FULLMEMCHECKS) + tprintf ("\n"); + tprintf ("%d chunks in use, total size=%d bytes\n", + (int) usedcount, usedsize * sizeof (MEMUNION)); + tprintf ("%d chunks free, total size=%d bytes\n", + (int) freecount, freesize * sizeof (MEMUNION)); + tprintf ("Largest free fragment=%d bytes\n", + biggest * sizeof (MEMUNION)); + } + totusedcount += usedcount; //grand totals + totusedsize += usedsize; + totfreecount += freecount; + totfreesize += freesize; + if (biggest > totbiggest) + totbiggest = biggest; + totblocksize += block->blockend - block->blockstart; + } + if (level >= MEMCHECKS) { + tprintf ("%d total blocks in use, total size=%d bytes\n", + blockcount, totblocksize * sizeof (MEMUNION)); + tprintf ("%d total chunks in use, total size=%d bytes\n", + (int) totusedcount, totusedsize * sizeof (MEMUNION)); + tprintf ("%d total chunks free, total size=%d bytes\n", + (int) totfreecount, totfreesize * sizeof (MEMUNION)); + tprintf ("Largest free fragment=%d bytes\n", + totbiggest * sizeof (MEMUNION)); + } + if (level >= MEMCHECKS) + display_counts(); +} + + +/********************************************************************** + * MEM_ALLOCATOR::alloc_p + * + * Allocate permanent space which will never be returned. + * This space is allocated from the top end of a memory block to + * avoid the fragmentation which would result from alternate use + * of alloc_mem for permanent and temporary blocks. + **********************************************************************/ + +void *MEM_ALLOCATOR::alloc_p( //permanent space + INT32 count, //block size to allocate + void *caller //ptr to caller + ) { + MEMBLOCK *block; //current block + MEMUNION *chunk; //current chunk + + if (count < 1 || count > biggestblock) + //request too big + MEMTOOBIG.error ("alloc_mem_p", ABORT, "%d", (int) count); + + count += sizeof (MEMUNION) - 1;//round up to word + count /= sizeof (MEMUNION); + count++; //and add one + if (topblock == NULL) { + topblock = new_block (count);//get first block + currblock = topblock; + if (topblock == NULL) { + check_mem ("alloc_mem_p returning NULL", MEMCHECKS); + return NULL; + } + } + block = topblock; //current block + do { + chunk = block->topchunk; + if (chunk->size < count) + block = block->next; //try next block + } + //until all tried + while (chunk->size < count && block != topblock); + if (chunk->size < count) { //still no good + chunk = (MEMUNION *) alloc ((count - 1) * sizeof (MEMUNION), caller); + //try last resort + if (chunk != NULL) + return chunk; + check_mem ("alloc_mem_p returning NULL", MEMCHECKS); + return NULL; + } + block->upperspace -= count; //less above freechunk + if (chunk->size > count) { + chunk->size -= count; + chunk += chunk->size; + } + chunk->size = -count; //mark as in use + if (mem_mallocdepth > 0) { + set_owner(chunk, caller); + } + else { + chunk->owner = 0; + chunk->age = 0; + } + return chunk + 1; //created chunk +} + + +/********************************************************************** + * MEM_ALLOCATOR::alloc + * + * Return a pointer to a buffer of count bytes aligned for any type. + **********************************************************************/ + +void *MEM_ALLOCATOR::alloc( //get memory + INT32 count, //no of bytes to get + void *caller //ptr to caller + ) { + MEMBLOCK *block; //current block + MEMUNION *chunk; //current chunk + INT32 chunksize; //size of free chunk + MEMUNION *chunkstart; //start of free chunk + + if (count < 1 || count > biggestblock) + MEMTOOBIG.error ("alloc_mem", ABORT, "%d", (int) count); + //request too big + + count += sizeof (MEMUNION) - 1;//round up to word + count /= sizeof (MEMUNION); + count++; //and add one + if (currblock == NULL) { + //get first block + currblock = new_block (count); + topblock = currblock; + if (currblock == NULL) { + check_mem ("alloc_mem returning NULL", MEMCHECKS); + return NULL; + } + } + block = currblock; //current block + if (block->upperspace <= block->lowerspace) { + //restart chunklist + block->freechunk = block->blockstart; + block->upperspace += block->lowerspace; + block->lowerspace = 0; //correct space counts + } + chunk = block->freechunk; //current free chunk + if (chunk->size < count) { //big enough? + do { + //search for free chunk + chunk = block->find_chunk (count); + if (chunk->size < count) + block = block->next; //try next block + } + //until all tried + while (chunk->size < count && block != currblock); + if (chunk->size < count) { //still no good + //get a new block + currblock = new_block (count); + topblock = currblock; //set perms here too + if (currblock == NULL) { + check_mem ("alloc_mem returning NULL", MEMCHECKS); + return NULL; + } + block = currblock; + chunk = block->freechunk; //bound to be big enough + } + } + chunkstart = chunk; //start of chunk + if (chunk == block->topchunk && chunk + count != block->blockend) + block->topchunk += count; //top has moved + block->upperspace -= count; //upper part used + chunksize = chunk->size; //size of free chunk + chunk->size = -count; //mark as used + chunk += count; //next free space + totalmem -= count; //no of free elements + if (chunksize > count) //bigger than exact? + //remaining space + chunk->size = chunksize - count; + else if (chunk == block->blockend) { + chunk = block->blockstart; //restart block + block->upperspace = block->lowerspace; + block->lowerspace = 0; //fix space counts + } + block->freechunk = chunk; //next free block + if (mem_mallocdepth > 0) { + set_owner(chunkstart, caller); + } + else { + chunkstart->owner = 0; + chunkstart->age = 0; + } + chunkstart++; //start of block + return chunkstart; //pointer to block +} + + +/********************************************************************** + * MEM_ALLOCATOR::set_owner + * + * Set the owner and time stamp of the block and check if needed. + **********************************************************************/ + +void MEM_ALLOCATOR::set_owner( //get memory + MEMUNION *chunkstart, //chunk to set + void *caller //ptr to caller + ) { + UINT16 callindex; //hash code + + callindex = hash_caller (caller); + chunkstart->owner = callindex; + //store evidence + chunkstart->age = malloc_serial; + malloc_minor_serial++; + if (malloc_minor_serial >= malloc_div_ratio) { + malloc_minor_serial = 0; + malloc_serial++; //count calls + if (malloc_serial == 0) { + //wrap around + reduce_counts(); //fix serial numbers + malloc_serial = MAX_INT16 + 1; + //all worth double + malloc_div_ratio += malloc_div_ratio; + } + } + malloc_auto_count++; + if (mem_checkfreq > 0 && malloc_auto_count >= (UINT32) mem_checkfreq) { + malloc_auto_count = 0; + check_mem ("Auto check", MEMCHECKS); + } +} + + +/********************************************************************** + * MEM_ALLOCATOR::dealloc + * + * Free a block allocated by alloc (or alloc_p). + * It checks that the pointer is legal and maintains counts of the + * amount of free memory above and below the current free pointer. + **********************************************************************/ + +void MEM_ALLOCATOR::dealloc( //free memory + void *oldchunk, //chunk to free + void *caller //ptr to caller + ) { + MEMUNION *chunk; //current chunk + MEMBLOCK *block; //current block + + if (oldchunk == NULL) + FREENULLPTR.error ("free_mem", ABORT, NULL); + chunk = (MEMUNION *) oldchunk; + block = currblock; //current block + if (block == NULL) + NOTMALLOCMEM.error ("free_mem", ABORT, NULL); + do { + block = block->next; + } + //outside the block + while ((chunk - block->blockstart < 0 || block->blockend - chunk <= 0) + && block != currblock); + + if (chunk - block->blockstart < 0 || block->blockend - chunk <= 0) + //in no block + NOTMALLOCMEM.error ("free_mem", ABORT, NULL); + + chunk--; //point to size + if (chunk->size == 0) + //zero size + FREEILLEGALPTR.error ("free_mem", ABORT, NULL); + else if (chunk->size > 0) + //already free + FREEFREEDBLOCK.error ("free_mem", ABORT, NULL); + chunk->size = -chunk->size; //mark it free + if (mem_freedepth > 0 && callers != NULL) { + //count calls + callers[chunk->owner].count_freeer (caller); + } + totalmem += chunk->size; //total free memory + if (chunk - block->freechunk < 0) + //extra below + block->lowerspace += chunk->size; + else + //extra above + block->upperspace += chunk->size; +} + + +/********************************************************************** + * MEM_ALLOCATOR::new_block + * + * Gets a new big block of memory from malloc for use by alloc_mem. + **********************************************************************/ + +MEMBLOCK *MEM_ALLOCATOR::new_block( //get new big block + INT32 minsize //minimum size + ) { + MEMBLOCK *newblock; //new block + + if (blockcount >= MAXBLOCKS) { + //can't have another + NOMOREBLOCKS.error ("mem_new_block", LOG, NULL); + return NULL; + } + if (mem_checkfreq != 0) { + tprintf ("\nGetting new block due to request size of %d", + minsize * sizeof (MEMUNION)); + tprintf (" from %d from %d from %d from %d from %d\n", + trace_caller (3), trace_caller (4), trace_caller (5), + trace_caller (6), trace_caller (7)); + check_mem ("Getting new block", MEMCHECKS); + } + //get a new one + newblock = &memblocks[blockcount++]; + while (memsize < minsize) + memsize *= 4; //go up in sizes + //get a big block + newblock->blockstart = (MEMUNION *) + malloc (memsize * sizeof (MEMUNION)); + if (newblock->blockstart == NULL) { + NOMOREMEM.error ("mem_new_block", LOG, NULL); + + #ifdef __UNIX__ + raise(SIGTTOU); //hangup for js + #endif + return NULL; + } + //end of block + newblock->blockend = newblock->blockstart + memsize; + //first free chunk + newblock->freechunk = newblock->blockstart; + newblock->topchunk = newblock->blockstart; + newblock->lowerspace = 0; + newblock->upperspace = memsize;//amount available + //set pointer + newblock->freechunk->size = memsize; + newblock->freechunk->owner = 0; + newblock->freechunk->age = 0; + + totalmem += memsize; //total assigned mem + + if (memsize < maxsize) + memsize *= 4; //successively bigger + if (currblock == NULL) { + newblock->next = newblock; //first block + } + else { + //insert in list + newblock->next = currblock->next; + currblock->next = newblock; + } + return newblock; //new block +} + + +/********************************************************************** + * MEMBLOCK::find_chunk + * + * Find a chunk within the block which is big enough for the given request + **********************************************************************/ + +MEMUNION *MEMBLOCK::find_chunk( //find free chunk + INT32 count //size required + ) { + MEMUNION *chunk; //current chunk + INT32 chunksize; //size of free chunk + MEMUNION *chunkstart; //start of free chunk + INT32 spaceshift; //shift in lowerspace + + if (upperspace <= lowerspace) { + freechunk = blockstart; //restart chunklist + upperspace += lowerspace; + lowerspace = 0; //correct space counts + } + chunk = freechunk; //current free chunk + if (chunk->size < count) { //big enough? + spaceshift = 0; + do { + while (chunk->size < 0) { //find free chunk + chunk -= chunk->size; //skip forward + if (chunk == blockend) { + chunk = blockstart; //restart block + //gone back to start + spaceshift = -lowerspace; + } + if (chunk == freechunk) + return chunk; //gone all round & failed + } + chunkstart = chunk; //start of chunk + chunksize = chunk->size;; + chunk += chunk->size; + while (chunk != blockend //until end + && chunk->size > 0) { //or used + chunksize += chunk->size;//coalesce free blocks + //gone all round + if (chunk == freechunk) { + //ensure it is at end + freechunk += chunk->size; + upperspace -= chunk->size; + lowerspace += chunk->size; + spaceshift -= chunk->size; + } + if (chunk == topchunk) //got back to end one + topchunk = chunkstart; //end one bigger + chunk += chunk->size; //get next block + } + //new big block + chunkstart->size = chunksize; + if (chunksize < count) + spaceshift += chunksize; //skipping free block + if (chunk == blockend) { + chunk = blockstart; //back to start + if (freechunk == blockend) { + freechunk = blockstart;//so is freechunk + upperspace += lowerspace; + lowerspace = 0; + spaceshift = 0; + } + else + //so is shift + spaceshift = -lowerspace; + } + } + while (chunksize < count && chunk != freechunk); + if (chunksize < count) + return chunk; //failed + lowerspace += spaceshift; //get space counts right + upperspace -= spaceshift; + freechunk = chunkstart; + return chunkstart; //success + } + return chunk; //easy +} + + +#ifdef __UNIX__ +/********************************************************************** + * trace_caller + * + * Return the return address of the caller at a given depth back. + * 0 gives the return address of the caller to trace_caller. + * S300 ONLY!! + **********************************************************************/ +//#pragma OPTIMIZE OFF /*force link*/ + +void *trace_caller( //trace stack + INT32 depth //depth to trace + ) { + #ifdef hp9000s800 + + unsigned sp, pc, rp; //registers + UWREC rec1; //for unwinder + UWREC rec2; + + sp = (unsigned) (&depth + 9); + pc = *(int *) (sp - 20); + rp = 0; + get_pcspace(&rec1, pc); + rec1.cur_frsize = 0xc0; + rec1.currlo = pc & ~3; + rec1.curdp = 0; + rec1.toprp = rp; + + while (depth > 0) { + if (U_get_previous_frame (&rec1, &rec2)) + return NULL; + rec1.currlo = rec2.currlo; + rec1.cur_frsize = rec2.cur_frsize; + rec1.cursp = rec2.cursp; + rec1.currls = rec2.currls; + rec1.curdp = rec2.curdp; + depth--; + } + return (void *) rec1.currlo; + #else + void *a6; //address register + + a6 = &depth - 2; + while (depth > 0) { + a6 = *(void **) a6; //follow chain + depth--; + } + return *((void **) a6 + 1); + #endif +} + + +//#pragma OPTIMIZE ON + +#else + +// Fake procedure for non-UNIX +void *trace_caller( //trace stack + INT32 depth //depth to trace + ) { + return NULL; +} +#endif + +/********************************************************************** + * identify_struct_owner + * + * Get an index into the table of owners of structures. + * Implemented very inefficiently, but only a debug tool! + **********************************************************************/ + +INT32 identify_struct_owner( //get table index + INT32 struct_count, //cell size + const char *name //name of type + ) { + INT32 index; //index to structure + + for (index = 0; index < name_counts[struct_count] + && strcmp (name, owner_names[struct_count][index]); index++); + if (index < MAX_CLASSES) { + if (index == name_counts[struct_count]) { + name_counts[struct_count]++; + owner_names[struct_count][index] = name; + owner_counts[struct_count][index] = 0; + } + } + return index; +} + + +/********************************************************************** + * check_struct + * + * Check a particular structure size for consistency. + **********************************************************************/ + +void check_struct( //check a structure + INT8 level, //print control + INT32 count //no of bytes + ) { + MEMUNION *element; //current element + MEMUNION *block; //current block + INT32 struct_count; //no of required structs + INT32 block_count; //no of structure blocks + INT32 free_count; //size of freelist*/ + INT32 name_index; //named holder + INT32 named_total; //total held by names + + //no of MEMUNIONS-1 + struct_count = (count - 1) / sizeof (MEMUNION); + if (struct_count < 0 || struct_count >= MAX_STRUCTS) + //request too big + MEMTOOBIG.error ("check_struct", ABORT, "%d", (int) count); + + free_count = 0; //size of freelist + //count blocks + for (block_count = 0, block = struct_blocks[struct_count]; block != NULL; block = block->ptr, block_count++); + if (block_count > 0) { + //scan freelist + for (element = free_structs[struct_count]; element != NULL; element = element->ptr) + free_count++; + if (level >= MEMCHECKS) { + tprintf ("No of structs of size %d in use=%d,", + (int) count, (int) structs_in_use[struct_count]); + tprintf (" %d free", free_count); + tprintf (" in %d blocks, total space=%d\n", + (int) block_count, + block_count * STRUCT_BLOCK_SIZE * sizeof (MEMUNION)); + for (named_total = 0, name_index = 0; + name_index < name_counts[struct_count]; name_index++) { + tprintf ("No held by %s=%d\n", + owner_names[struct_count][name_index], + owner_counts[struct_count][name_index]); + named_total += owner_counts[struct_count][name_index]; + } + tprintf ("Total held by names=%d\n", named_total); + } + } + if (structs_in_use[struct_count] + free_count + != block_count * (STRUCT_BLOCK_SIZE / (struct_count + 1) - 1)) + BADSTRUCTCOUNT.error ("check_struct", ABORT, "%d+%d=%d", + structs_in_use[struct_count], free_count, + block_count * (STRUCT_BLOCK_SIZE / + (struct_count + 1) - 1)); +} + + +/********************************************************************** + * check_structs + * + * Reports statistics on each maintained structure type by calling + * free_struct(NULL) on each. Only active structure types are reported. + **********************************************************************/ + +void check_structs( //count in use on structs + INT8 level //print control + ) { + INT8 index; //index to structs + + for (index = 1; index <= MAX_STRUCTS; index++) + //check number allocated + check_struct (level, index * sizeof (MEMUNION)); +} + + +/********************************************************************** + * new_struct_block + * + * Allocate space for a new block of structures. The space is obtained + * from alloc_mem, and a freelist of such blocks is maintained for when + * the individual structure types get completely freed. + **********************************************************************/ + +void *new_struct_block() { //allocate memory + MEMUNION *element; //current element + MEMUNION *returnelement; //return value + + returnelement = free_block; + if (returnelement == NULL) { + //need a new block + element = + (MEMUNION *) alloc_mem_p (STRUCT_BLOCK_SIZE * sizeof (MEMUNION)); + if (element == NULL) + return NULL; //can't get more + returnelement = element; //going to return 1st + } + else { + //new free one + free_block = returnelement->ptr; + } + return returnelement; //free cell +} + + +/********************************************************************** + * old_struct_block + * + * Free memory allocated by new_struct_block. The block is returned + * to a freelist ready for a new call to new_struct_block. + * This saves confusion over freeing "permanent" blocks, yet + * allows them to be recycled for different structures. + **********************************************************************/ + +void old_struct_block( //free a structure block + MEMUNION *deadblock //block to free + ) { + if (deadblock != NULL) { + deadblock->ptr = free_block; //add to freelist + free_block = deadblock; + free_struct_blocks++; + } + if (free_struct_blocks > MAX_FREE_S_BLOCKS) { + MEMUNION *next_block; //next in list + deadblock = free_block; + do { + next_block = deadblock->ptr; + free_mem(deadblock); //really free it + deadblock = next_block; + } + while (deadblock != NULL); + free_struct_blocks = 0; + free_block = NULL; + } +} diff --git a/ccutil/memblk.h b/ccutil/memblk.h new file mode 100644 index 0000000000..210578dd13 --- /dev/null +++ b/ccutil/memblk.h @@ -0,0 +1,189 @@ +/********************************************************************** + * File: memblk.h (Formerly memblock.h) + * Description: Enhanced instrumented memory allocator implemented as a class. + * Author: Ray Smith + * Created: Tue Jan 21 17:13:39 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef MEMBLK_H +#define MEMBLK_H + +#include "varable.h" + +#define MAXBLOCKS 16 /*max allowed to grab */ +#define MAX_STRUCTS 20 //no of units maintained +#define MAX_CLASSES 24 //max classes of each size +#define MAX_FREE_S_BLOCKS 10 //max free list before all freed +#define STRUCT_BLOCK_SIZE 2521 +#define MAX_CHUNK 262144 //max single chunk +#define FIRSTSIZE 16384 //size of first block +#define LASTSIZE 262144 //biggest size to use +#define BIGSIZE 2100000 //size of big blocks +#define MAX_BIGCHUNK 20000000 //max chunk of big mem + +//#define TESTING_BIGSTUFF //define for big tests +//#define COUNTING_CLASS_STRUCTURES + +class MEMUNION +{ + public: + union + { + MEMUNION *ptr; //next chunk + INT32 size; //chunk size + }; + UINT16 owner; //owner of chunk + UINT16 age; //age of chunk +}; + +class MEMBLOCK +{ + public: + MEMUNION * blockstart; /*start of block */ + MEMUNION *blockend; /*end of block */ + MEMUNION *freechunk; /*next free chunk */ + MEMUNION *topchunk; /*top free chunk */ + MEMBLOCK *next; /*next block in chain */ + INT32 upperspace; /*space above freechunk */ + INT32 lowerspace; /*space below freechunk */ + + MEMUNION *find_chunk( //find free chunk + INT32 count); //size required +}; + +class FREE_CALL +{ + public: + void *freeer; //return addr + INT32 count; //no of frees + FREE_CALL() { //constructor + freeer = NULL; + count = 0; + } +}; +class MALLOC_CALL +{ + public: + void *caller; //return addr + FREE_CALL *free_list; //freeer counts + INT32 *counts; //no of blocks + INT32 free_bits; //bits in free table + + MALLOC_CALL() { //constructor + caller = NULL; + free_list = NULL; + counts = NULL; + free_bits = 0; + } + void count_freeer( //check a structure + void *addr); //return address + + void init_freeers(); //check a structure +}; + +class MEM_ALLOCATOR +{ + public: + INT16 blockcount; //blocks in use + UINT16 malloc_serial; //serial allocation + MEMBLOCK *topblock; //block for permanents + MEMBLOCK *currblock; //current block + MALLOC_CALL *callers; //hash table of callers + void *(*malloc) (INT32); //external allocator + void (*free) (void *); //external free + INT32 maxsize; //biggest block + INT32 biggestblock; //biggest chunk + INT32 totalmem; //total free memory + INT32 memsize; //current block size + UINT32 malloc_div_ratio; //scaling of malloc_serial + UINT32 malloc_minor_serial; //scaling counter + UINT32 malloc_auto_count; //counts auto checks + INT32 call_bits; //size of table + INT32 entries; //size of table + //all memory blocks + MEMBLOCK memblocks[MAXBLOCKS]; + + void init ( //initialize + void *(*ext_malloc) (INT32),//external source + void (*ext_free) (void *), //external free + INT32 firstsize, //size of first block + INT32 lastsize, //size of last block + INT32 maxchunk); //biggest request + + void *alloc( //allocator + INT32 size, //size of chunk + void *caller); //ptr to caller + void *alloc_p( //allocator + INT32 size, //size of chunk + void *caller); //ptr to caller + void dealloc( //deallocator + void *ptr, //mem to free + void *caller); //ptr to caller + void check( //check chunks + const char *string, //message + INT8 level); //amount of checks + + void reduce_counts(); //divide by 2 + void display_counts(); //count up + MEMBLOCK *new_block( //get new big block + INT32 minsize); //minimum size + UINT16 hash_caller( //check a structure + void *addr); //return address + + private: + void init_callers(); //check a structure + void set_owner( //set owner & date + MEMUNION *chunkstart, //chunk to set + void *caller); //ptr to caller +}; +extern MEM_ALLOCATOR big_mem; +extern MEM_ALLOCATOR main_mem; + //heads of freelists +extern MEMUNION *free_structs[MAX_STRUCTS]; + //number issued +extern INT32 structs_in_use[MAX_STRUCTS]; + //number issued +extern INT32 blocks_in_use[MAX_STRUCTS]; + //head of block lists +extern MEMUNION *struct_blocks[MAX_STRUCTS]; +extern INT32 owner_counts[MAX_STRUCTS][MAX_CLASSES]; + +extern INT_VAR_H (mem_mallocdepth, 0, "Malloc stack depth to trace"); +extern INT_VAR_H (mem_mallocbits, 8, "Log 2 of hash table size"); +extern INT_VAR_H (mem_freedepth, 0, "Free stack dpeth to trace"); +extern INT_VAR_H (mem_freebits, 8, "Log 2 of hash table size"); +extern INT_VAR_H (mem_countbuckets, 16, "No of buckets for histogram"); +extern INT_VAR_H (mem_checkfreq, 0, +"Calls to alloc_mem between owner counts"); + +void *trace_caller( //trace stack + INT32 depth //depth to trace + ); +INT32 identify_struct_owner( //get table index + INT32 struct_count, //cell size + const char *name //name of type + ); +void check_struct( //check a structure + INT8 level, //print control + INT32 count //no of bytes + ); +void check_structs( //count in use on structs + INT8 level //print control + ); +void *new_struct_block(); //allocate memory +void old_struct_block( //free a structure block + MEMUNION *deadblock //block to free + ); +#endif diff --git a/ccutil/memry.cpp b/ccutil/memry.cpp new file mode 100644 index 0000000000..5396240c9d --- /dev/null +++ b/ccutil/memry.cpp @@ -0,0 +1,531 @@ +/********************************************************************** + * File: memry.c (Formerly memory.c) + * Description: Memory allocation with builtin safety checks. + * Author: Ray Smith + * Created: Wed Jan 22 09:43:33 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#ifdef __UNIX__ +#include +#endif +#include "stderr.h" +#include "tprintf.h" +#include "memblk.h" +#include "memry.h" + +//#define COUNTING_CLASS_STRUCTURES + +/********************************************************************** + * new + * + * Replace global new to get at memory leaks etc. + **********************************************************************/ +/* +void* operator new( //allocate memory +size_t size //amount to allocate +) +{ + if (size==0) + { + err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Zero requested of new"); + size=1; + } + return alloc_big_mem(size); +} + +void operator delete( //free memory +void* addr //mem to free +) +{ + free_big_mem(addr); +}*/ + +/********************************************************************** + * check_mem + * + * Check consistency of all memory controlled by alloc_mem. + **********************************************************************/ + +DLLSYM void check_mem( //check consistency + const char *string, //context message + INT8 level //level of check + ) { + big_mem.check (string, level); + main_mem.check (string, level); + check_structs(level); +} + + +/********************************************************************** + * alloc_string + * + * Allocate space for a string. The space can only be used for chars as + * it is not aligned. Allocation is guaranteed to be fast and not cause + * fragmentation for small strings (upto 10*worst alignment). Calls for + * larger strings will be satisfied with alloc_mem. + * Use free_string to free the space from alloc_string. + **********************************************************************/ + +DLLSYM char *alloc_string( //allocate string + INT32 count //no of chars required + ) { +#ifdef RAYS_MALLOC + char *string; //allocated string + + if (count < 1 || count > MAX_CHUNK) { + tprintf ("Invalid size %d requested of alloc_string", count); + return NULL; + } + + count++; //add size byte + if (count <= MAX_STRUCTS * sizeof (MEMUNION)) { + string = (char *) alloc_struct (count, "alloc_string"); + //get a fast structure + if (string == NULL) { + tprintf ("No memory for alloc_string"); + return NULL; + } + string[0] = (INT8) count; //save its length + } + else { + //get a big block + string = (char *) alloc_mem (count); + if (string == NULL) { + tprintf ("No memory for alloc_string"); + return NULL; + } + string[0] = 0; //mark its id + } + return &string[1]; //string for user +#else + return static_cast(malloc(count)); +#endif +} + + +/********************************************************************** + * free_string + * + * Free a string allocated by alloc_string. + **********************************************************************/ + +DLLSYM void free_string( //free a string + char *string //string to free + ) { +#ifdef RAYS_MALLOC + if (((ptrdiff_t) string & 3) == 1) { //one over word + string--; //get id marker + if (*string == 0) { + free_mem(string); //generally free it + return; + } + else if (*string <= MAX_STRUCTS * sizeof (MEMUNION)) { + //free structure + free_struct (string, *string, "alloc_string"); + return; + } + } + tprintf ("Non-string given to free_string"); +#else + free(string); +#endif +} + + +/********************************************************************** + * alloc_struct + * + * Allocate space for a structure. This function is designed to be + * fast and fragmentation free for arbitrary combinations of small + * objects. (Upto 40 bytes in length.) + * It can be used for any size of object up to 512K, but you must use + * free_struct to release the memory it gives. alloc_mem is better + * for arbitrary data blocks of large size (>40 bytes.) + * alloc_struct always aborts if the allocation fails. + **********************************************************************/ + +DLLSYM void * +alloc_struct ( //allocate memory +INT32 count, //no of chars required +#if defined COUNTING_CLASS_STRUCTURES +const char *name //name of type +#else +const char * //name of type +#endif +) { +#ifdef RAYS_MALLOC + MEMUNION *element; //current element + MEMUNION *returnelement; //return value + INT32 struct_count; //no of required structs + INT32 blocksize; //no of structs in block + INT32 index; //index to structure + + if (count < 1 || count > MAX_CHUNK) { + tprintf ("Invalid size %d requested of alloc_struct", count); + return NULL; + } + + // tprintf("Allocating structure of size %d\n",count); + //no of MEMUNIONS-1 + struct_count = (count - 1) / sizeof (MEMUNION); + if (struct_count < MAX_STRUCTS) { + //can do fixed sizes + #ifdef COUNTING_CLASS_STRUCTURES + if (name != NULL) { + index = identify_struct_owner (struct_count, name); + if (index < MAX_CLASSES) + owner_counts[struct_count][index]++; + } + #endif + //head of freelist + returnelement = free_structs[struct_count]; + if (returnelement == NULL) { + //need a new block + //get one + element = (MEMUNION *) new_struct_block (); + if (element == NULL) { + tprintf ("No memory to satisfy request for %d", (int) count); + return NULL; + } + //add to block list + element->ptr = struct_blocks[struct_count]; + struct_blocks[struct_count] = element; + blocks_in_use[struct_count]++; + element++; //free cell + returnelement = element; //going to return 1st + blocksize = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1; + + for (index = 1; index < blocksize; index++) { + //make links + element->ptr = element + struct_count + 1; + element += struct_count + 1; + } + element->ptr = NULL; //end of freelist + } + //new free one + free_structs[struct_count] = returnelement->ptr; + //count number issued + structs_in_use[struct_count]++; + } + else { + //just get some + returnelement = (MEMUNION *) alloc_mem (count); + if (returnelement == NULL) { + tprintf ("No memory to satisfy request for %d", (int) count); + return NULL; + } + } + return returnelement; //free cell +#else + return malloc(count); +#endif +} + + +/********************************************************************** + * free_struct + * + * Free memory allocated by alloc_struct. The size must be supplied. + **********************************************************************/ + +DLLSYM void +free_struct ( //free a structure +void *deadstruct, //structure to free +INT32 count, //no of bytes +#if defined COUNTING_CLASS_STRUCTURES +const char *name //name of type +#else +const char * //name of type +#endif +) { +#ifdef RAYS_MALLOC + MEMUNION *end_element; //current element + MEMUNION *element; //current element + MEMUNION *prev_element; //previous element + MEMUNION *prev_block; //previous element + MEMUNION *nextblock; //next block in list + MEMUNION *block; //next block in list + INT32 struct_count; //no of required structs + INT32 index; //to structure counts + + if (count < 1 || count > MAX_CHUNK) { + tprintf ("Invalid size %d requested of free_struct", count); + return; + } + + // tprintf("Freeing structure of size %d\n",count); + //no of MEMUNIONS-1 + struct_count = (count - 1) / sizeof (MEMUNION); + + if (deadstruct == NULL) { + //not really legal + check_struct(MEMCHECKS, count); + } + else { + if (struct_count < MAX_STRUCTS) { + //can do fixed sizes + #ifdef COUNTING_CLASS_STRUCTURES + if (name != NULL) { + index = identify_struct_owner (struct_count, name); + if (index < MAX_CLASSES) { + owner_counts[struct_count][index]--; + ASSERT_HOST (owner_counts[struct_count][index] >= 0); + } + } + #endif + element = (MEMUNION *) deadstruct; + //add to freelist + element->ptr = free_structs[struct_count]; + free_structs[struct_count] = element; + //one less in use + structs_in_use[struct_count]--; + if (structs_in_use[struct_count] == 0) { + index = 0; + for (element = struct_blocks[struct_count]; + element != NULL; element = nextblock) { + //traverse and destroy + nextblock = element->ptr; + //free all the blocks + old_struct_block(element); + index++; + } + //none left any more + struct_blocks[struct_count] = NULL; + //no free structs + free_structs[struct_count] = NULL; + blocks_in_use[struct_count] = 0; + } + else if (structs_in_use[struct_count] < 0) { + tprintf ("Negative number of structs of size %d in use", + (int) count); + } + else if (structs_in_use[struct_count] < blocks_in_use[struct_count]) { + prev_block = NULL; + for (block = struct_blocks[struct_count]; + block != NULL; block = nextblock) { + nextblock = block; + index = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1; + end_element = block + STRUCT_BLOCK_SIZE; + for (element = free_structs[struct_count]; + element != NULL; element = element->ptr) { + if (element > nextblock && element < end_element) { + index--; + if (index == 0) + break; + } + } + if (index == 0) { + index = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1; + for (element = + free_structs[struct_count], prev_element = NULL; + element != NULL; element = element->ptr) { + if (element > nextblock && element < end_element) { + index--; + if (prev_element != NULL) + prev_element->ptr = element->ptr; + else + free_structs[struct_count] = element->ptr; + if (index == 0) + break; + } + else + prev_element = element; + } + if (prev_block != NULL) + prev_block->ptr = block->ptr; + else + struct_blocks[struct_count] = block->ptr; + nextblock = block->ptr; + blocks_in_use[struct_count]--; + //free all the blocks + old_struct_block(block); + } + else { + prev_block = block; + //traverse and destroy + nextblock = block->ptr; + } + } + } + } + else + free_mem(deadstruct); //free directly + } +#else + free(deadstruct); +#endif +} + + +/********************************************************************** + * alloc_mem_p + * + * Allocate permanent space which will never be returned. + * This space is allocated from the top end of a memory block to + * avoid the fragmentation which would result from alternate use + * of alloc_mem for permanent and temporary blocks. + **********************************************************************/ + +//#ifdef __UNIX__ +//#pragma OPT_LEVEL 0 +//#endif +DLLSYM void *alloc_mem_p( //allocate permanent space + INT32 count //block size to allocate + ) { + #ifdef RAYS_MALLOC + #ifdef TESTING_BIGSTUFF + if (main_mem.biggestblock == 0) + main_mem.init (alloc_big_mem, free_big_mem, + FIRSTSIZE, LASTSIZE, MAX_CHUNK); + #else + if (main_mem.biggestblock == 0) + main_mem.init ((void *(*)(INT32)) malloc, free, + FIRSTSIZE, LASTSIZE, MAX_CHUNK); + #endif + if (mem_mallocdepth > 0) + return main_mem.alloc_p (count, trace_caller (mem_mallocdepth)); + else + return main_mem.alloc_p (count, NULL); + #else + return malloc ((size_t) count); + #endif +} + + +/********************************************************************** + * alloc_mem + * + * Return a pointer to a buffer of count bytes aligned for any type. + **********************************************************************/ + +DLLSYM void *alloc_mem( //get some memory + INT32 count //no of bytes to get + ) { + #ifdef RAYS_MALLOC + #ifdef TESTING_BIGSTUFF + if (main_mem.biggestblock == 0) + main_mem.init (alloc_big_mem, free_big_mem, + FIRSTSIZE, LASTSIZE, MAX_CHUNK); + #else + if (main_mem.biggestblock == 0) + main_mem.init ((void *(*)(INT32)) malloc, free, + FIRSTSIZE, LASTSIZE, MAX_CHUNK); + #endif + if (mem_mallocdepth > 0) + return main_mem.alloc (count, trace_caller (mem_mallocdepth)); + else + return main_mem.alloc (count, NULL); + #else + return malloc ((size_t) count); + #endif +} + + +/********************************************************************** + * alloc_big_mem + * + * Return a pointer to a buffer of count bytes aligned for any type. + **********************************************************************/ + +DLLSYM void *alloc_big_mem( //get some memory + INT32 count //no of bytes to get + ) { + #ifdef TESTING_BIGSTUFF + if (big_mem.biggestblock == 0) + big_mem.init ((void *(*)(INT32)) malloc, free, + BIGSIZE, BIGSIZE, MAX_BIGCHUNK); + if (mem_mallocdepth > 0) + return big_mem.alloc (count, trace_caller (mem_mallocdepth)); + else + return big_mem.alloc (count, NULL); + #else + return malloc ((size_t) count); + #endif +} + + +/********************************************************************** + * alloc_big_zeros + * + * Return a pointer to a buffer of count bytes aligned for any type. + **********************************************************************/ + +DLLSYM void *alloc_big_zeros( //get some memory + INT32 count //no of bytes to get + ) { + #ifdef TESTING_BIGSTUFF + if (big_mem.biggestblock == 0) + big_mem.init ((void *(*)(INT32)) malloc, free, + BIGSIZE, BIGSIZE, MAX_BIGCHUNK); + void *buf; //return value + + if (mem_mallocdepth > 0) + buf = big_mem.alloc (count, trace_caller (mem_mallocdepth)); + else + buf = big_mem.alloc (count, NULL); + memset (buf, 0, count); + return buf; + #else + return calloc ((size_t) count, 1); + #endif +} + + +/********************************************************************** + * free_mem + * + * Free a block allocated by alloc_mem (or alloc_mem_p). + * It checks that the pointer is legal and maintains counts of the + * amount of free memory above and below the current free pointer. + **********************************************************************/ + +DLLSYM void free_mem( //free mem from alloc_mem + void *oldchunk //chunk to free + ) { + #ifdef RAYS_MALLOC + if (mem_freedepth > 0 && main_mem.callers != NULL) + main_mem.dealloc (oldchunk, trace_caller (mem_freedepth)); + else + main_mem.dealloc (oldchunk, NULL); + #else + free(oldchunk); + #endif +} + + +/********************************************************************** + * free_big_mem + * + * Free a block allocated by alloc_big_mem. + * It checks that the pointer is legal and maintains counts of the + * amount of free memory above and below the current free pointer. + **********************************************************************/ + +DLLSYM void free_big_mem( //free mem from alloc_mem + void *oldchunk //chunk to free + ) { + #ifdef TESTING_BIGSTUFF + if (mem_freedepth > 0 && main_mem.callers != NULL) + big_mem.dealloc (oldchunk, trace_caller (mem_freedepth)); + else + big_mem.dealloc (oldchunk, NULL); + #else + free(oldchunk); + #endif +} diff --git a/ccutil/memry.h b/ccutil/memry.h new file mode 100644 index 0000000000..8f65ed7f07 --- /dev/null +++ b/ccutil/memry.h @@ -0,0 +1,192 @@ +/********************************************************************** + * File: memry.h (Formerly memory.h) + * Description: Header file for basic memory allocation/deallocation. + * Author: Ray Smith + * Created: Tue May 8 16:03:48 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef MEMRY_H +#define MEMRY_H + +#include +#include "host.h" + +#define JUSTCHECKS 0 /*just check consistency */ +#define MEMCHECKS 1 /*report totals */ +#define FULLMEMCHECKS 2 /*report on all blocks */ + +#ifdef __MSW32__ +#define NEWDELETE /*replace new & delete*/\ + void *operator new( /*fast allocator*/\ + size_t size, /*size of object*/\ + const char* file=NULL, /*filename*/\ + INT32 line=0) /*line number*/\ + {\ + return alloc_struct(size); /*simple to do*/\ + }\ +\ + void operator delete( /*fast destructor*/\ + void *deadstruct, /*thing to free*/\ + size_t size) /*sizeof struct*/\ + {\ + free_struct(deadstruct,size); /*free it*/\ + }\ + +#define NEWDELETE2(name) /*replace new & delete*/\ + void *operator new( /*fast allocator*/\ + size_t size, /*size of object*/\ + const char* file=NULL, /*filename*/\ + INT32 line=0) /*line number*/\ + {\ + return alloc_struct(size,#name); /*simple to do*/\ + }\ +\ + void operator delete( /*fast destructor*/\ + void *deadstruct, /*thing to free*/\ + size_t size) /*sizeof struct*/\ + {\ + free_struct(deadstruct,size,#name); /*free it*/\ + }\ + + +#undef NEWDELETE +#define NEWDELETE +#undef NEWDELETE2 +#define NEWDELETE2(name) + +#else +#define NEWDELETE /*replace new & delete*/\ + void *operator new( /*fast allocator*/\ + size_t size) /*size of object*/\ + {\ + return alloc_struct(size); /*simple to do*/\ + }\ +\ + void operator delete( /*fast destructor*/\ + void *deadstruct, /*thing to free*/\ + size_t size) /*sizeof struct*/\ + {\ + free_struct(deadstruct,size); /*free it*/\ + }\ + +#define NEWDELETE2(name) /*replace new & delete*/\ + void *operator new( /*fast allocator*/\ + size_t size) /*size of object*/\ + {\ + return alloc_struct(size,#name); /*simple to do*/\ + }\ +\ + void operator delete( /*fast destructor*/\ + void *deadstruct, /*thing to free*/\ + size_t size) /*sizeof struct*/\ + {\ + free_struct(deadstruct,size,#name); /*free it*/\ + }\ + +#endif +/********************************************************************** + * ALLOC_2D_ARRAY + * + * Create a dynamic 2D array. + **********************************************************************/ + +#define ALLOC_2D_ARRAY(x,y,mem,ptrs,type) /*make 2d array*/\ +{ \ + INT32 TMP_i; \ + mem=(type*)alloc_mem((x)*(y)*sizeof(type)); /*get memory*/\ + ptrs=(type**)alloc_mem((x)*sizeof(type*)); /*get ptrs*/\ + for (TMP_i=0;TMP_i<(x);TMP_i++)\ + ptrs[TMP_i]=mem+(y)*TMP_i; /*set ptrs*/\ +} \ + +/********************************************************************** + * FREE_2D_ARRAY + * + * Destroy a 2D array created by ALLOC_2D_ARRAY + **********************************************************************/ + +#define FREE_2D_ARRAY(mem,ptrs) /*free a 2D array*/\ +{ \ + free_mem(mem); /*free the memory*/\ + free_mem(ptrs); /*and the ptrs*/\ +} \ + +/********************************************************************** + * ALLOC_BIG_2D_ARRAY + * + * Create a dynamic 2D array. Use a memory allocator that allows + * allocation of bigger chunks. + **********************************************************************/ + +#define ALLOC_BIG_2D_ARRAY(x,y,mem,ptrs,type) /*make 2d array*/\ +{ \ + INT32 TMP_i; \ + mem=(type*)alloc_big_mem((x)*(y)*sizeof(type)); /*get memory*/\ + ptrs=(type**)alloc_big_mem((x)*sizeof(type*)); /*get ptrs*/\ + for (TMP_i=0;TMP_i<(x);TMP_i++)\ + ptrs[TMP_i]=mem+(y)*TMP_i; /*set ptrs*/\ +} \ + +/********************************************************************** + * FREE_BIG_2D_ARRAY + * + * Destroy a 2D array created by ALLOC_BIG_2D_ARRAY + **********************************************************************/ + +#define FREE_BIG_2D_ARRAY(mem,ptrs) /*free a 2D array*/\ +{ \ + free_big_mem(mem); /*free the memory*/\ + free_big_mem(ptrs); /*and the ptrs*/\ +} \ + +extern DLLSYM void check_mem( //check consistency + const char *string, //context message + INT8 level //level of check + ); + //allocate string +extern DLLSYM char *alloc_string(INT32 count //no of chars required + ); +extern DLLSYM void free_string( //free a string + char *string //string to free + ); + //allocate memory +extern DLLSYM void *alloc_struct ( +INT32 count, //no of chars required +const char *name = NULL //class name +); +extern DLLSYM void free_struct ( //free a structure +void *deadstruct, //structure to free +INT32 count, //no of bytes +const char *name = NULL //class name +); +extern DLLSYM void *alloc_mem_p( //allocate permanent space + INT32 count //block size to allocate + ); +extern DLLSYM void *alloc_mem( //get some memory + INT32 count //no of bytes to get + ); + //get some memory +extern DLLSYM void *alloc_big_mem(INT32 count //no of bytes to get + ); + //get some memory +extern DLLSYM void *alloc_big_zeros(INT32 count //no of bytes to get + ); +extern DLLSYM void free_mem( //free mem from alloc_mem + void *oldchunk //chunk to free + ); +extern DLLSYM void free_big_mem( //free mem from alloc_mem + void *oldchunk //chunk to free + ); +#endif diff --git a/ccutil/memryerr.h b/ccutil/memryerr.h new file mode 100644 index 0000000000..6c8a025230 --- /dev/null +++ b/ccutil/memryerr.h @@ -0,0 +1,38 @@ +/********************************************************************** + * File: memryerr.h (Formerly: memerr.h) + * Description: File defining error messages for memory functions. + * Author: Ray Smith + * Created: Mon Aug 13 12:51:03 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef MEMRYERR_H +#define MEMRYERR_H + +#include "errcode.h" + +const ERRCODE MEMTOOBIG = "Memory request too big"; +const ERRCODE NOMOREBLOCKS = "Max total memory blocks exceeded"; +const ERRCODE NOMOREMEM = "No more memory available from malloc"; +const ERRCODE FREENULLPTR = "Attempt to free memory NULL pointer"; +const ERRCODE NOTMALLOCMEM = +"Attempt to free memory not belonging to memalloc"; +const ERRCODE FREEILLEGALPTR = "Pointer or memory corrupted"; +const ERRCODE FREEFREEDBLOCK = "Memory block already marked free"; +const ERRCODE BADMEMCHUNKS = "Inconsistency in memory chunks"; +const ERRCODE BADSTRUCTCOUNT = "Memory incorrect freelist length"; +const ERRCODE NEGATIVE_USED_STRUCTS = +"Negative number of used memory structures"; +const ERRCODE NOTASTRING = "Illegal pointer for memory strfree"; +#endif diff --git a/ccutil/mfcpch.cpp b/ccutil/mfcpch.cpp new file mode 100644 index 0000000000..60375c9f9d --- /dev/null +++ b/ccutil/mfcpch.cpp @@ -0,0 +1,5 @@ +// mfcpch.cpp : source file that includes just the standard includes +// elist.pch will be the pre-compiled header +// mfcpch.obj will contain the pre-compiled type information + +#include "mfcpch.h" //precompiled headers diff --git a/ccutil/mfcpch.h b/ccutil/mfcpch.h new file mode 100644 index 0000000000..d9d217238d --- /dev/null +++ b/ccutil/mfcpch.h @@ -0,0 +1,37 @@ +// mfcpch.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// +// For Unix and mac the file does nothing. It needs to be included in all cpp +// files for compatibility with the PC pre-compiled header mechanism. +#ifdef __MSW32__ +#ifdef __IPEREGDLL +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 +#include +#include +#else +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // mfc core and standard components +#include // mfc extensions + +#ifndef _AFX_NO_OLE_SUPPORT +#include // mfc ole classes +#include // mfc ole dialog classes +#include // mfc ole automation classes +#endif // _AFX_NO_OLE_SUPPORT + +#ifndef _AFX_NO_DB_SUPPORT +#include // mfc odbc database classes +#endif // _AFX_NO_DB_SUPPORT + +#ifndef _AFX_NO_DAO_SUPPORT +#include // mfc dao database classes +#endif // _AFX_NO_DAO_SUPPORT + +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // mfc support for windows 95 common controls +#endif // _AFX_NO_AFXCMN_SUPPORT +#endif +#endif diff --git a/ccutil/ndminx.h b/ccutil/ndminx.h new file mode 100644 index 0000000000..00d1fc79fe --- /dev/null +++ b/ccutil/ndminx.h @@ -0,0 +1,25 @@ +/********************************************************************** + * File: ndminx.h (Formerly ndminmax.h) + * Description: Extended ascii chars + * Author: Phil Cheatle + * Created: Mon Mar 29 14:46:01 BST 1993 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef NDMINX_H +#define NDMINX_H + +#define MAX(a,b) ( (a>b) ? a : b ) +#define MIN(a,b) ( (am_pszExeName);\ + argsin[1]=strdup(theapp->m_lpCmdLine);\ +/*allocate memory for the args. There can never be more than half*/\ +/*the total number of characters in the arguments.*/\ + argv=(char**)malloc(((strlen(argsin[0])+strlen(argsin[1]))/2+1)*sizeof(char*));\ +\ +/*now construct argv as it should be for C.*/\ + argc=parse_args(2,argsin,argv);\ +\ +/*call main(argc,argv) here*/\ + exit_code=real_main(argc,(const char **)argv);\ +\ +\ +/*now get rid of the main app window*/\ + if (theapp!=NULL && theapp->m_pMainWnd!=NULL)\ + PostMessage(theapp->m_pMainWnd->m_hWnd,WM_QUIT,0,0);\ + free(argsin[0]);\ + free(argsin[1]);\ + free(argv);\ + global_exit_code=exit_code;\ + return exit_code;\ +}\ +\ +INT32 real_main(INT32 ARGC,const char* ARGV[])\ + +#else + +#define REALLY_DECLARE_MAIN(ARGC,ARGV)\ +\ +/**********************************************************************\ +* parse_args\ +*\ +* Turn a list of args into a new list of args with each separate\ +* whitespace spaced string being an arg.\ +**********************************************************************/\ +\ +INT32 parse_args( /*refine arg list*/\ +INT32 argc, /*no of input args*/\ +char *argv[], /*input args*/\ +char *arglist[] /*output args*/\ +)\ +{\ + INT32 argcount; /*converted argc*/\ + char *testchar; /*char in option string*/\ + INT32 arg; /*current argument*/\ +\ + argcount=0; /*no of options*/\ + for (arg=0;arg +#ifdef __MSW32__ +#include +#endif +#include "host.h" + +/*Maximum lengths of various strings*/ +#define MAX_FONT_NAME 34 /*name of font */ +#define MAX_OCR_NAME 32 /*name of engine */ +#define MAX_OCR_VERSION 17 /*version code of engine */ + +/*Image parameters*/ +#define MIN_IMAGE_SIZE 64 /*smallest image that will be passed */ +#define IMAGE_ROUNDING 32 /*all sizes are multiple of this */ + +#if defined(__SLOW_TIMES__) +/*Maximum timeouts of various functions (in secs)*/ +#define STARTUP_TIMEOUT 100 /*start of OCR engine */ +#define SHUTDOWN_TIMEOUT 50 /*end of OCR engine */ +#define SENDIM_TIMEOUT 50 /*send of image */ +#define RELEASE_TIMEOUT 50 /*release of semaphore */ +#define READIM_TIMEOUT 100 /*read of image */ +#define READTEXT_TIMEOUT 50 /*read of text */ +#define PROGRESS_TIMEOUT 30 /*progress every 3 seconds */ +#define BADTIMES_TIMEOUT 7 /*max lack of progress */ +#else +/*Maximum timeouts of various functions (in secs)*/ +#define STARTUP_TIMEOUT 10 /*start of OCR engine */ +#define SHUTDOWN_TIMEOUT 6 /*end of OCR engine */ +#define SENDIM_TIMEOUT 5 /*send of image */ +#define RELEASE_TIMEOUT 5 /*release of semaphore */ +#define READIM_TIMEOUT 10 /*read of image */ +#define READTEXT_TIMEOUT 5 /*read of text */ +#define PROGRESS_TIMEOUT 3 /*progress every 3 seconds */ +#define BADTIMES_TIMEOUT 7 /*max lack of progress */ +#endif + +/*language definitions are identical to RTF*/ +#define LANGE_NONE 0x0400 /*no language */ +#define LANGE_ALBANIAN 0x041c /*Albanian */ +#define LANGE_BRITISH 0x0809 /*International English */ +#define LANGE_BULGARIAN 0x0402 /*Bulgarian */ +#define LANGE_CROATIAN 0x041a /*Croatian(latin alphabet) */ +#define LANGE_CZECH 0x0405 /*Czech */ +#define LANGE_DANISH 0x0406 /*Danish */ +#define LANGE_DUTCH 0x0413 /*Dutch */ +#define LANGE_FINNISH 0x040b /*Finnish */ +#define LANGE_FRENCH 0x040c /*French */ +#define LANGE_GERMAN 0x0407 /*German */ +#define LANGE_GREEK 0x0408 /*Greek */ +#define LANGE_HUNGARIAN 0x040e /*Hungarian */ +#define LANGE_ITALIAN 0x0410 /*Italian */ +#define LANGE_JAPANESE 0x0411 /*Japanese */ +#define LANGE_KOREAN 0x0412 /*Korean */ +#define LANGE_NORWEGIAN 0x0414 /*Bokmal */ +#define LANGE_POLISH 0x0415 /*Polish */ +#define LANGE_PORTUGESE 0x0416 /*Brazilian Portugese */ +#define LANGE_ROMANIAN 0x0418 /*Romanian */ +#define LANGE_RUSSIAN 0x0419 /*Russian */ +#define LANGE_SCHINESE 0x0804 /*Simplified Chinese */ +#define LANGE_SLOVAK 0x041b /*Slovak */ +#define LANGE_SPANISH 0x040a /*Castilian */ +#define LANGE_SWEDISH 0x041d /*Swedish */ +#define LANGE_TCHINESE 0x0404 /*Traditional Chinese */ +#define LANGE_TURKISH 0x041f /*Turkish */ +#define LANGE_USENGLISH 0x0409 /*American */ + +/*font family definitions are identical to RTF*/ +#define FFAM_NONE 0 /*unknown */ +#define FFAM_ROMAN 1 /*serifed prop */ +#define FFAM_SWISS 2 /*sans-serif prop */ +#define FFAM_MODERN 3 /*fixed pitch */ + +/*character set definitions are identical to RTF*/ +#define CHSET_ANSI 0 /*Ansi efigs */ +#define CHSET_SHIFT_JIS 128 /*JIS X 0208-1990 */ +#define CHSET_KOREAN 129 /*KS C 5601-1992 */ +#define CHSET_SCHINESE 134 /*GB 2312-80 */ +#define CHSET_BIG5 136 /*Big Five */ +#define CHSET_CYRILLIC 204 /*Cyrillic */ +#define CHSET_EEUROPE 238 /*Eastern Europe */ + +/*pitch set definitions are identical to RTF*/ +#define PITCH_DEF 0 /*default */ +#define PITCH_FIXED 1 /*fixed pitch */ +#define PITCH_VAR 2 /*variable pitch */ + +/*Bitmasks for character enhancements. +OR these together for enhancement in ocr_append_char*/ +#define EUC_BOLD 1 /*bold character */ +#define EUC_ITALIC 2 /*italic char */ +#define EUC_UNDERLINE 4 /*underlined char */ +#define EUC_SUBSCRIPT 8 /*subscript char */ +#define EUC_SUPERSCRIPT 16 /*superscript char */ + +/*enum for character rendering direction*/ +enum OCR_CHAR_DIRECTION +{ + OCR_CDIR_RIGHT_LEFT, /*right to left horizontal */ + OCR_CDIR_LEFT_RIGHT, /*left to right horizontal */ + OCR_CDIR_TOP_BOTTOM, /*top to bottom vertical */ + OCR_CDIR_BOTTOM_TOP /*bottom to top vertical */ +}; + +/*enum for line rendering direction*/ +enum OCR_LINE_DIRECTION +{ + OCR_LDIR_DOWN_RIGHT, /*horizontal lines go down */ + /*vertical lines go right */ + OCR_LDIR_UP_LEFT /*horizontal lines go up */ +}; + +/*enum for newline type*/ +enum OCR_NEWLINE_TYPE +{ + OCR_NL_NONE, /*not a newline */ + OCR_NL_NEWLINE, /*this is a newline but not new para */ + OCR_NL_NEWPARA /*this is a newline and a new para */ +}; + +/*error codes that can be returned from the API functions other than OKAY and HPERR*/ +#define OCR_API_NO_MEM (-2) /*filled output buffer */ +#define OCR_API_BAD_CHAR (-3) /*whitespace sent to ocr_append_char */ +#define OCR_API_BAD_STATE (-4) /*invalid call sequence */ + +/*error codes used for passing errors back to the HP side*/ +enum OCR_ERR_CODE +{ + OCR_ERR_NONE, /*no error */ + OCR_ERR_CLEAN_EXIT, /*no error */ + OCR_ERR_NO_MEM, /*out of memory */ + OCR_ERR_FILE_READ, /*failed to read data file */ + OCR_ERR_TMP_WRITE, /*failed to write temp file */ + OCR_ERR_TMP_READ, /*failed to read temp file */ + OCR_ERR_BAD_DLL, /*missing or invalid dll subcomponent */ + OCR_ERR_BAD_EXE, /*missing or invalid exe subcomponent */ + OCR_ERR_BAD_LOAD, /*failed to load subcomponent */ + OCR_ERR_BAD_LANG, /*unable to recognize requested language */ + OCR_ERR_BAD_STATE, /*engine did call out of sequence */ + OCR_ERR_INTERNAL1, /*internal error type 1 */ + OCR_ERR_INTERNAL2, /*internal error type 1 */ + OCR_ERR_INTERNAL3, /*internal error type 1 */ + OCR_ERR_INTERNAL4, /*internal error type 1 */ + OCR_ERR_INTERNAL5, /*internal error type 1 */ + OCR_ERR_INTERNAL6, /*internal error type 1 */ + OCR_ERR_INTERNAL7, /*internal error type 1 */ + OCR_ERR_INTERNAL8, /*internal error type 1 */ + OCR_ERR_TIMEOUT /*timed out in comms */ +}; /*for calls to ocr_error */ + +/********************************************************************** + * EFONT_DESC + * Description of one font. + * The information required is basically that used by RTF. + * The name may be either a valid font on the system or the empty string. + **********************************************************************/ + +typedef struct /*font description */ +{ + UINT16 language; /*default language */ + UINT8 font_family; /*serif/not, fixed/not */ + UINT8 char_set; /*character set standard */ + UINT8 pitch; /*fixed or prop */ + INT8 name[MAX_FONT_NAME + 1]; /*plain ascii name */ +} EFONT_DESC; /*font description */ + +/********************************************************************** + * EOCR_DESC + * Description of the OCR engine provided at startup. + * The name and version may be reported to the user at some point. + * The fonts array should indicate the fonts that the OCR system + * can recognize. + **********************************************************************/ + +typedef struct /*startup info */ +{ + INT32 protocol; /*interface version */ + UINT32 font_count; /*number of fonts */ + UINT16 language; /*default language */ + UINT16 name[MAX_OCR_NAME + 1]; /*name of engine */ + /*version of engine */ + UINT16 version[MAX_OCR_VERSION + 1]; + EFONT_DESC fonts[1]; /*array of fonts */ +} EOCR_DESC; /*startup info */ + +/********************************************************************** + * ESTRIP_DESC + * Description of the image strip as it is passed to the engine. + * The image is always 1 bit, with 1=black. + * The width is always a multiple of 32, so padding is always OK. + * The height of the full image is always a multiple of 32. + * The top y coordinate is 0, and increases down. + * The top leftmost pixel is in the most significant bit of the first byte. + **********************************************************************/ + +typedef struct /*bitmap strip */ +{ + INT16 x_size; /*width in pixels */ + INT16 y_size; /*of full image */ + INT16 strip_size; /*of this strip */ + INT16 resolution; /*pixels per inch */ + UINT8 data[8]; /*image data */ +} ESTRIP_DESC; /*bitmap strip */ + +/********************************************************************** + * EANYCODE_CHAR + * Description of a single character. The character code is defined by + * the character set of the current font. + * Output text is sent as an array of these structures. + * Spaces and line endings in the output are represented in the + * structures of the surrounding characters. They are not directly + * represented as characters. + * The first character in a word has a positive value of blanks. + * Missing information should be set to the defaults in the comments. + * If word bounds are known, but not character bounds, then the top and + * bottom of each character should be those of the word. The left of the + * first and right of the last char in each word should be set. All other + * lefts and rights should be set to -1. + * If set, the values of right and bottom are left+width and top+height. + * Most of the members come directly from the parameters to ocr_append_char. + * The formatting member uses the enhancement parameter and combines the + * line direction stuff into the top 3 bits. + * The coding is 0=RL char, 1=LR char, 2=DR NL, 3=UL NL, 4=DR Para, + * 5=UL Para, 6=TB char, 7=BT char. API users do not need to know what + * the coding is, only that it is backwards compatible with the previous + * version. + **********************************************************************/ + +typedef struct /*single character */ +{ + UINT16 char_code; /*character itself */ + INT16 left; /*of char (-1) */ + INT16 right; /*of char (-1) */ + INT16 top; /*of char (-1) */ + INT16 bottom; /*of char (-1) */ + INT16 font_index; /*what font (0) */ + UINT8 confidence; /*0=perfect, 100=reject (0/100) */ + UINT8 point_size; /*of char, 72=i inch, (10) */ + INT8 blanks; /*no of spaces before this char (1) */ + UINT8 formatting; /*char formatting (0) */ +} EANYCODE_CHAR; /*single character */ + +/********************************************************************** + * ETEXT_DESC + * Description of the output of the OCR engine. + * This structure is used as both a progress monitor and the final + * output header, since it needs to be a valid progress monitor while + * the OCR engine is storing its output to shared memory. + * During progress, all the buffer info is -1. + * Progress starts at 0 and increases to 100 during OCR. No other constraint. + * Every progress callback, the OCR engine must set ocr_alive to 1. + * The HP side will set ocr_alive to 0. Repeated failure to reset + * to 1 indicates that the OCR engine is dead. + * If the cancel function is not null then it is called with the number of + * user words found. If it returns true then operation is cancelled. + **********************************************************************/ +typedef bool (*CANCEL_FUNC)(void* cancel_this, int words); + +typedef struct /*output header */ +{ + INT16 count; /*chars in this buffer(0) */ + INT16 progress; /*percent complete increasing (0-100) */ + INT8 more_to_come; /*true if not last */ + INT8 ocr_alive; /*ocr sets to 1, HP 0 */ + INT8 err_code; /*for errcode use */ + CANCEL_FUNC cancel; /*returns true to cancel */ + void* cancel_this; /*this or other data for cancel*/ + clock_t end_time; /*time to stop if not 0*/ + EANYCODE_CHAR text[1]; /*character data */ +} ETEXT_DESC; /*output header */ + +#ifdef __MSW32__ +/********************************************************************** + * ESHM_INFO + * This data structure is used internally to the API to hold the handles + * to the operating system tools used for interprocess communications. + * API users do not access this structure directly. + **********************************************************************/ +typedef struct /*shared mem info */ +{ + HANDLE shm_hand; /*handle to shm */ + HANDLE mutex; /*alive check */ + HANDLE ocr_sem; /*ocr semaphore */ + HANDLE hp_sem; /*hp semaphore */ + void *shm_mem; /*shared memory */ + INT32 shm_size; /*size of shm */ +} ESHM_INFO; /*shared mem info */ +#elif defined (__MAC__) +typedef struct /*shared mem info */ +{ + Boolean mutex; /*alive check */ + Boolean ocr_sem; /*ocr semaphore */ + Boolean hp_sem; /*hp semaphore */ + void *shm_mem; /*shared memory */ + INT32 shm_size; /*size of shm */ + INT16 language; + + // Process management information follows: + ProcessSerialNumber IPEProcess; + ProcessSerialNumber OCRProcess; +} ESHM_INFO; +#elif defined (__UNIX__) +typedef struct /*shared mem info */ +{ + void *shm_mem; /*shared memory */ + INT32 shm_size; /*size of shm */ +} ESHM_INFO; +#endif +#endif diff --git a/ccutil/ocrshell.cpp b/ccutil/ocrshell.cpp new file mode 100644 index 0000000000..0f814e5875 --- /dev/null +++ b/ccutil/ocrshell.cpp @@ -0,0 +1,759 @@ +/********************************************************************** + * File: ocrshell.cpp + * Description: Code for the OCR side of the OCR API. + * Author: Hewlett-Packard Co + * + * (C) Copyright 1996, Hewlett-Packard Co. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +/********************************************************************** + * This file contains code for the OCR side of the HP OCR interface. + * The code is designed to be used with either an ANSI C or C++ compiler. + * The structures are designed to allow them to be used with any + * structure alignment upto 8. + **********************************************************************/ + +#include "mfcpch.h" +#include "ocrshell.h" +#include "tprintf.h" +#include + +#define EXTERN + +#ifdef __UNIX__ +EXTERN ESHM_INFO shm; /*info on shm */ +#define TICKS 1 +#endif + +#ifdef __MSW32__ +EXTERN ESHM_INFO shm; /*info on shm */ +#define TICKS 1000 +#endif + +#ifdef __MAC__ + +#if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__) +#pragma import on +#endif + +extern volatile ESHM_INFO shm; /*info on shm */ +extern unsigned short WaitForSingleObject( /*"C" */ + volatile Boolean &semaphore, + unsigned long timeout); +extern unsigned short ReleaseSemaphore( /*"C" */ + volatile Boolean &semaphore); +#if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__) +#pragma import reset +#endif +#define WAIT_OBJECT_0 1 +#define TICKS 60 +#endif + +typedef enum { + OCS_UNINIT, /*uninitialized */ + OCS_SETUP_SHM, /*shm setup done */ + OCS_SETUP_INFO, /*startinfo sent */ + OCS_READING_STRIPS, /*read first but more to come */ + OCS_READ_STRIPS, /*read all but no monitor yet */ + OCS_RECOGNIZING, /*OCR incomplete */ + OCS_SENDING_TEXT, /*sent buffer but more to come */ + OCS_DEAD /*disconnected */ +} OCR_STATE; + +/* forward declarations - not in .h file as not needed outside this file*/ +INT16 ocr_internal_shutdown(); /*closedown */ +INT16 wait_for_mutex(); /*wait for HP to be ready */ +INT16 wait_for_hp( /*wait for semaphore */ + INT32 timeout /*in seconds */ + ); +INT16 release_mutex(); /*release mutex */ +INT16 release_ocr(); /*release semaphore */ + +static INT32 font_count = 0; /*number of fonts */ +static INT16 lines_read = 0; /*no read in this image */ + /*current state */ +static OCR_STATE ocr_state = OCS_UNINIT; + +#ifdef __MAC__ +pascal short TerminateOCR(AppleEvent *theEvent, + AppleEvent *theReply, + long refCon) { + ocr_internal_shutdown(); + ExitToShell(); + +} +#endif + +/********************************************************************** + * ocr_open_shm + * + * Attempt to connect to the shared memory segment and semaphores used + * in talking to the OCR engine. Called from OCR engine. + * The parameters are the command line arguments in order. + **********************************************************************/ +#ifdef __MAC__ +INT16 +ocr_open_shm (UINT16 * lang) +#else +INT16 +ocr_open_shm ( /*open the shm */ +const char *shm_h, /*handle of shm */ +const char *shm_size, /*size of shm segment */ +const char *mutex_h, /*hp mutex */ +const char *ocr_h, /*ocr semaphore */ +const char *hp_h, /*hp semaphore */ +const char *lang_str, /*language */ +UINT16 * lang /*required language */ +) +#endif +{ + font_count = 0; /*no fonts yet */ + #ifdef __MAC__ + if (shm.OCRProcess.lowLongOfPSN && shm.OCRProcess.highLongOfPSN) + return HPERR; + *lang = shm.language; + GetCurrentProcess (&shm.OCRProcess); + if (WakeUpProcess (&shm.IPEProcess)) + ExitToShell(); + AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, + (AEEventHandlerUPP) TerminateOCR, 0, FALSE); + #else + if (lang != NULL) + /*get language */ + *lang = (UINT16) strtol (lang_str, NULL, 10); + #endif + if (ocr_state != OCS_UNINIT) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + #ifdef __MSW32__ + shm.shm_size = strtol (shm_size, NULL, 10); + /*convert to handle */ + shm.shm_hand = (HANDLE) strtol (shm_h, NULL, 10); + shm.shm_mem = MapViewOfFile (shm.shm_hand, FILE_MAP_WRITE, 0, 0, 0); + if (shm.shm_mem == NULL) + return HPERR; /*failed */ + /*convert to handle */ + shm.mutex = (HANDLE) strtol (mutex_h, NULL, 10); + /*convert to handle */ + shm.ocr_sem = (HANDLE) strtol (ocr_h, NULL, 10); + /*convert to handle */ + shm.hp_sem = (HANDLE) strtol (hp_h, NULL, 10); + #endif + + ocr_state = OCS_SETUP_SHM; /*record state */ + return OKAY; + +} + + +/********************************************************************** + * ocr_error + * + * Inform the HP side of an error. + * The OCR engine should do any cleanup of its own and exit aferwards. + * Uses the current state to determine how to send it and cleanup. + **********************************************************************/ + +void ocr_error( /*send an error code */ + OCR_ERR_CODE code /*error code */ + ) { + ESTRIP_DESC *strip = (ESTRIP_DESC *) shm.shm_mem; + /*strip info */ + ETEXT_DESC *monitor = (ETEXT_DESC *) shm.shm_mem; + /*progress monitor */ + + switch (ocr_state) { + case OCS_UNINIT: /*uninitialized */ + case OCS_DEAD: /*uninitialized */ + return; /*can't do anything else */ + case OCS_SETUP_SHM: /*shm setup done */ + if (font_count < 1) + font_count = 1; + ocr_setup_startinfo_ansi (-code, LANGE_NONE, "", ""); + /*report error */ + break; + case OCS_SETUP_INFO: /*startinfo sent */ + if (ocr_get_first_image_strip () == NULL) + break; /*disconnected */ + case OCS_READING_STRIPS: /*read first but more to come */ + strip->x_size = -code; /*report error */ + release_ocr(); /*send ack */ + release_mutex(); + break; + case OCS_READ_STRIPS: /*read all but no monitor yet */ + monitor->count = 0; /*chars in this buffer(-1) */ + monitor->progress = 0; /*percent complete increasing (0-100) */ + /*text not complete */ + monitor->more_to_come = FALSE; + monitor->ocr_alive = TRUE; /*ocr sets to 1, hp 0 */ + monitor->err_code = -code; /*report error */ + monitor->cancel = FALSE; /*0=continue, 1=cancel */ + release_ocr(); /*send ack */ + break; + case OCS_RECOGNIZING: /*OCR incomplete */ + case OCS_SENDING_TEXT: /*sent buffer but more to come */ + monitor->err_code = -code; /*report error */ + release_ocr(); /*send ack */ + } + ocr_internal_shutdown(); /*get ready for exit */ +} + + +/********************************************************************** + * ocr_append_fontinfo + * + * Initialize one of the font descriptors. + **********************************************************************/ + +INT16 ocr_append_fontinfo( /*put info into shm */ + UINT16 language, /*default language */ + UINT8 font_family, /*serif/not, fixed/not */ + UINT8 char_set, /*character set standard */ + UINT8 pitch, /*fixed or prop */ + const char *name /*plain ascii name */ + ) { + EOCR_DESC *desc; /*ocr engine info */ + int index; /*char index */ + INT32 font_index; /*which font */ + + if (ocr_state != OCS_SETUP_SHM) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + + /*turn to right type */ + desc = (EOCR_DESC *) shm.shm_mem; + if (font_count > + (INT32) ((shm.shm_size - sizeof (EOCR_DESC)) / sizeof (EFONT_DESC))) + return OCR_API_NO_MEM; /*insufficient space */ + font_index = font_count++; /*add a font */ + /*setup structure */ + desc->fonts[font_index].language = language; + /*setup structure */ + desc->fonts[font_index].font_family = font_family; + /*setup structure */ + desc->fonts[font_index].char_set = char_set; + /*setup structure */ + desc->fonts[font_index].pitch = pitch; + if (name != NULL) { + for (index = 0; index < MAX_FONT_NAME && name[index] != 0; index++) + desc->fonts[font_index].name[index] = name[index]; + } + else + index = 0; + desc->fonts[font_index].name[index] = 0; + return OKAY; +} + + +/********************************************************************** + * ocr_setup_startinfo + * + * Setup the info on the OCR engine. Uses 16 bit chars to name the + * engine. + **********************************************************************/ + +INT16 ocr_setup_startinfo( /*put info into shm */ + INT32 protocol, /*interface version */ + UINT16 language, /*default language */ + const UINT16 *name, /*name of engine */ + const UINT16 *version /*version of engine */ + ) { + EOCR_DESC *desc; /*ocr engine info */ + int index; /*char index */ + INT16 result; /*from open */ + + if (ocr_state != OCS_SETUP_SHM || font_count < 1) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + + /*turn to right type */ + desc = (EOCR_DESC *) shm.shm_mem; + desc->protocol = protocol; /*setup structure */ + desc->font_count = font_count; + desc->language = language; + for (index = 0; index < MAX_OCR_NAME && name[index] != 0; index++) + desc->name[index] = name[index]; + desc->name[index] = 0; + for (index = 0; index < MAX_OCR_VERSION && version[index] != 0; index++) + desc->version[index] = version[index]; + desc->version[index] = 0; + + result = release_ocr (); + if (result != OKAY) + return result; + ocr_state = OCS_SETUP_INFO; /*record state */ + return OKAY; +} + + +/********************************************************************** + * ocr_setup_startinfo_ansi + * + * Setup the info on the OCR engine. Uses 8 bit chars to name the + * engine. + **********************************************************************/ + +INT16 ocr_setup_startinfo_ansi( /*put info into shm */ + UINT32 protocol, /*interface version */ + UINT16 language, /*default language */ + const char *name, /*name of engine */ + const char *version /*version of engine */ + ) { + EOCR_DESC *desc; /*ocr engine info */ + int index; /*char index */ + INT16 result; /*from open */ + + if (ocr_state != OCS_SETUP_SHM || font_count < 1) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + + /*turn to right type */ + desc = (EOCR_DESC *) shm.shm_mem; + desc->protocol = protocol; /*setup structure */ + desc->font_count = font_count; + desc->language = language; + for (index = 0; index < MAX_OCR_NAME && name[index] != 0; index++) + desc->name[index] = name[index]; + desc->name[index] = 0; + for (index = 0; index < MAX_OCR_VERSION && version[index] != 0; index++) + desc->version[index] = version[index]; + desc->version[index] = 0; + + result = release_ocr (); + if (result != OKAY) + return result; + ocr_state = OCS_SETUP_INFO; /*record state */ + return OKAY; +} + + +/********************************************************************** + * ocr_get_first_image_strip + * + * Wait for the master to send the first image strip and return a + * pointer to it. The result is NULL if it is time to exit. + **********************************************************************/ + +ESTRIP_DESC *ocr_get_first_image_strip() { /*get image strip */ + ESTRIP_DESC *strip; /*strip info */ + INT16 result; /*of wait/release */ + + if (ocr_state != OCS_SETUP_INFO) { + tprintf ("Bad state reading strip"); + ocr_error(OCR_ERR_BAD_STATE); + return NULL; /*incorrect state */ + } + + /*strip info */ + strip = (ESTRIP_DESC *) shm.shm_mem; + lines_read = 0; + + result = wait_for_mutex (); + if (result != OKAY) { + tprintf ("Mutax wait failed reading strip"); + return NULL; /*HP dead */ + } + result = release_mutex (); + if (result != OKAY) { + tprintf ("Mutax release failed reading strip"); + return NULL; /*HP dead */ + } + result = wait_for_hp (READIM_TIMEOUT); + if (result != OKAY) { + tprintf ("Wait for HP failed reading strip"); + return NULL; /*HP dead */ + } + lines_read = strip->strip_size;/*lines read so far */ + if (lines_read < strip->y_size) + /*record state */ + ocr_state = OCS_READING_STRIPS; + else + ocr_state = OCS_READ_STRIPS; + if (strip->x_size == 0 || strip->y_size == 0) + return NULL; /*end of job */ + + return strip; +} + + +/********************************************************************** + * ocr_get_next_image_strip + * + * Wait for the master to send the next image strip and return a + * pointer to it. The result is NULL if it is time to exit. + **********************************************************************/ + +ESTRIP_DESC *ocr_get_next_image_strip() { /*get image strip */ + ESTRIP_DESC *strip; /*strip info */ + INT16 result; /*of wait/release */ + + if (ocr_state != OCS_READING_STRIPS) { + ocr_error(OCR_ERR_BAD_STATE); + return NULL; /*incorrect state */ + } + + /*strip info */ + strip = (ESTRIP_DESC *) shm.shm_mem; + result = release_ocr (); + if (result != OKAY) + return NULL; /*HP dead */ + result = wait_for_hp (READIM_TIMEOUT); + if (result != OKAY) + return NULL; /*HP dead */ + /*lines read so far */ + lines_read += strip->strip_size; + if (lines_read < strip->y_size) + /*record state */ + ocr_state = OCS_READING_STRIPS; + else + ocr_state = OCS_READ_STRIPS; + + return strip; +} + + +/********************************************************************** + * ocr_setup_monitor + * + * Setup the progress monitor. Call before starting the recognize task. + **********************************************************************/ + +ETEXT_DESC *ocr_setup_monitor() { /*setup monitor */ + ETEXT_DESC *monitor; /*progress monitor */ + + /*text info */ + monitor = (ETEXT_DESC *) shm.shm_mem; + monitor->count = 0; /*chars in this buffer(-1) */ + monitor->progress = 0; /*percent complete increasing (0-100) */ + monitor->more_to_come = TRUE; /*text not complete */ + monitor->ocr_alive = TRUE; /*ocr sets to 1, hp 0 */ + monitor->err_code = 0; /*used by ocr_error */ + monitor->cancel = FALSE; /*0=continue, 1=cancel */ + + if (release_ocr () != OKAY) + return NULL; /*release failed */ + + ocr_state = OCS_RECOGNIZING; /*record state */ + return monitor; +} + + +/********************************************************************** + * ocr_char_space + * + * Return the number of chars that can be fitted into the buffer. + **********************************************************************/ + +INT32 ocr_char_space() { /*put char into shm */ + ETEXT_DESC *buf; /*text buffer */ + int result; + + /*progress info */ + buf = (ETEXT_DESC *) shm.shm_mem; + result = + (shm.shm_size - sizeof (ETEXT_DESC)) / sizeof (EANYCODE_CHAR) - + buf->count + 1; + + // while (buf->hp_alive==-1) + // Sleep(50); /*wait for HP*/ + + return result; +} + + +/********************************************************************** + * ocr_append_char + * + * Add a character to the output. Returns OKAY if successful, OCR_API_NO_MEM + * if there was insufficient room in the buffer. + **********************************************************************/ + +INT16 ocr_append_char( /*put char into shm */ + UINT16 char_code, /*character itself */ + INT16 left, /*of char (-1) */ + INT16 right, /*of char (-1) */ + INT16 top, /*of char (-1) */ + INT16 bottom, /*of char (-1) */ + INT16 font_index, /*what font (-1) */ + UINT8 confidence, /*0=perfect, 100=reject (0/100) */ + UINT8 point_size, /*of char, 72=i inch, (10) */ + INT8 blanks, /*no of spaces before this char (1) */ + UINT8 enhancement, /*char enhancement (0) */ + OCR_CHAR_DIRECTION text_dir, /*rendering direction (OCR_CDIR_RIGHT_LEFT) */ + OCR_LINE_DIRECTION line_dir, /*line rendering direction (OCR_LDIR_DOWN_RIGHT) */ + OCR_NEWLINE_TYPE nl_type /*type of newline (if any) (OCR_NL_NONE) */ + ) { + ETEXT_DESC *buf; /*text buffer */ + int index; /*char index */ + INT16 result; /*of callback */ + + if (ocr_state != OCS_RECOGNIZING && ocr_state != OCS_SENDING_TEXT) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + + if (char_code == ' ' || char_code == '\n' || char_code == '\r' + || char_code == '\t') + return OCR_API_BAD_CHAR; /*illegal char */ + + /*progress info */ + buf = (ETEXT_DESC *) shm.shm_mem; + + result = + (shm.shm_size - sizeof (ETEXT_DESC)) / sizeof (EANYCODE_CHAR) - + buf->count; + if (result < 1) + return OCR_API_NO_MEM; /*insufficient room */ + + index = buf->count++; /*count of chars */ + /*setup structure */ + buf->text[index].char_code = char_code; + buf->text[index].left = left; /*setup structure */ + buf->text[index].right = right;/*setup structure */ + buf->text[index].top = top; /*setup structure */ + /*setup structure */ + buf->text[index].bottom = bottom; + /*setup structure */ + buf->text[index].font_index = font_index; + /*setup structure */ + buf->text[index].confidence = confidence; + /*setup structure */ + buf->text[index].point_size = point_size; + /*setup structure */ + buf->text[index].blanks = blanks; + if (nl_type == OCR_NL_NONE) { + if (text_dir == OCR_CDIR_TOP_BOTTOM || text_dir == OCR_CDIR_BOTTOM_TOP) + buf->text[index].formatting = (text_dir << 5) | 128; + /*setup structure */ + else + /*setup structure */ + buf->text[index].formatting = text_dir << 5; + } + else { + buf->text[index].formatting = (nl_type << 6) | (line_dir << 5); + /*setup structure */ + } + buf->text[index].formatting |= enhancement & (~EUC_FORMAT_MASK); + return OKAY; +} + + +/********************************************************************** + * ocr_send_text + * + * Send the text to the host and wait for the ack. + * Use this function after a sequence of ocr_append_char calls to + * actually sent the text to the master process. + * Set more to come TRUE if there is more text in this page, FALSE + * if the OCR engine is now ready to receive another image. + **********************************************************************/ + +INT16 ocr_send_text( /*send shm */ + BOOL8 more_to_come /*any text left */ + ) { + ETEXT_DESC *buf; /*text buffer */ + + if (ocr_state != OCS_RECOGNIZING && ocr_state != OCS_SENDING_TEXT) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + + /*progress info */ + buf = (ETEXT_DESC *) shm.shm_mem; + + /*setup structure */ + buf->more_to_come = more_to_come; + if (more_to_come) { + if ((buf->text[buf->count - 1].formatting >> 6) != OCR_NL_NEWLINE + && (buf->text[buf->count - 1].formatting >> 6) != OCR_NL_NEWPARA) { + /*force line end */ + buf->text[buf->count - 1].formatting &= 63; + buf->text[buf->count - 1].formatting |= OCR_NL_NEWLINE << 6; + } + } + else { + if (buf->count < 1) + ocr_append_char ('~', -1, -1, -1, -1, 0, 100, 10, 0, + 0, OCR_CDIR_RIGHT_LEFT, OCR_LDIR_DOWN_RIGHT, + OCR_NL_NEWPARA); + /*dummy character */ + else if ((buf->text[buf->count - 1].formatting >> 6) != OCR_NL_NEWPARA) { + /*force para end */ + buf->text[buf->count - 1].formatting &= 63; + buf->text[buf->count - 1].formatting |= OCR_NL_NEWPARA << 6; + } + } + + if (release_ocr () != OKAY) + return HPERR; /*release failed */ + if (wait_for_hp (READTEXT_TIMEOUT) != OKAY) + return HPERR; + if (more_to_come) { + buf->count = 0; /*setup structure */ + ocr_state = OCS_SENDING_TEXT;/*record state */ + } + else + ocr_state = OCS_SETUP_INFO; /*record state */ + return OKAY; +} + + +/********************************************************************** + * ocr_shutdown + * + * Closedown communications with the HP side and free up handles. + **********************************************************************/ + +INT16 ocr_shutdown() { /*closedown */ + #ifdef __MAC__ + shm.OCRProcess.lowLongOfPSN = kNoProcess; + shm.OCRProcess.highLongOfPSN = 0; + #endif + ocr_error(OCR_ERR_CLEAN_EXIT); /*signal exit */ + + return OKAY; +} + + +/********************************************************************** + * ocr_internal_shutdown + * + * Free up handles or whatever to clean up without attempting to communicate. + **********************************************************************/ + +INT16 ocr_internal_shutdown() { /*closedown */ + ocr_state = OCS_DEAD; /*record state */ + #ifdef __MSW32__ + if (shm.shm_mem != NULL) { + UnmapViewOfFile (shm.shm_mem); + CloseHandle (shm.shm_hand); /*no longer used */ + CloseHandle (shm.mutex); /*release handles */ + CloseHandle (shm.ocr_sem); + CloseHandle (shm.hp_sem); + shm.shm_mem = NULL; + } + #elif defined (__MAC__) + shm.OCRProcess.lowLongOfPSN = kNoProcess; + shm.OCRProcess.highLongOfPSN = 0; + #endif + return OKAY; +} + + +/********************************************************************** + * wait_for_mutex + * + * Wait for the HP side to release its mutex. + * The return value is HPERR if the HP side has terminated. + **********************************************************************/ + +INT16 wait_for_mutex() { /*wait for HP to be ready */ + INT16 result = HPERR; /*return code */ + #if defined (__MSW32__) || defined (__MAC__) + result = WaitForSingleObject (shm.mutex, (unsigned long) -1) + /*wait for thread to move */ + /*bad if timeout */ + == WAIT_OBJECT_0 ? OKAY : HPERR; + #endif + if (result != OKAY) + ocr_internal_shutdown(); + return result; +} + + +/********************************************************************** + * wait_for_hp + * + * Wait for the HP side to release its semaphore. + * The return value is HPERR if the timeout (in seconds) elapsed. + **********************************************************************/ + +INT16 wait_for_hp( /*wait for semaphore */ + INT32 timeout /*in seconds */ + ) { + INT16 result = HPERR; /*return code */ + #if defined (__MSW32__) || defined (__MAC__) + /*wait for thread to move */ + result = WaitForSingleObject (shm.hp_sem, timeout * TICKS) + /*bad if timeout */ + == WAIT_OBJECT_0 ? OKAY : HPERR; + #endif + if (result != OKAY) + ocr_internal_shutdown(); + return result; +} + + +/********************************************************************** + * release_mutex + * + * Release the HP mutex. + * The return value is OKAY if the call succeeds. + **********************************************************************/ + +INT16 release_mutex() { /*release mutex */ + INT16 result = HPERR; /*return code */ + #ifdef __MSW32__ + /*release it */ + result = ReleaseMutex (shm.mutex) ? OKAY : HPERR; + #elif defined (__MAC__) + /*release it */ + result = ReleaseSemaphore (shm.mutex) ? OKAY : HPERR; + #endif + if (result != OKAY) + ocr_internal_shutdown(); + return result; +} + + +/********************************************************************** + * release_ocr + * + * Release the OCR semaphore. + * The return value is OKAY if the call succeeds. + **********************************************************************/ + +INT16 release_ocr() { /*release semaphore */ + INT32 timeout; //time allowed + + timeout = RELEASE_TIMEOUT * TICKS; + #ifdef __MSW32__ + BOOL result = 0; //of release + do { + //release it + result = ReleaseSemaphore (shm.ocr_sem, 1, NULL); + if (result == FALSE) { + timeout -= 50; + Sleep (50); + } + } + while (result == FALSE && timeout > 0); + if (!result) + ocr_internal_shutdown(); + return OKAY; + #elif defined (__MAC__) + INT16 result = HPERR; /*return code */ + /*release it */ + result = ReleaseSemaphore (shm.ocr_sem) ? OKAY : HPERR; + + if (result != OKAY) + ocr_internal_shutdown(); + return result; + #elif defined (__UNIX__) + return 0; + #endif +} diff --git a/ccutil/ocrshell.h b/ccutil/ocrshell.h new file mode 100644 index 0000000000..4008ac2793 --- /dev/null +++ b/ccutil/ocrshell.h @@ -0,0 +1,191 @@ +/********************************************************************** + * File: ocrshell.h + * Description: Code for the OCR side of the OCR API. + * Author: Hewlett-Packard Co + * + * (C) Copyright 1996, Hewlett-Packard Co. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef OCRSHELL_H +#define OCRSHELL_H + +/********************************************************************** + * This file contains code for the OCR side of the HP OCR interface. + * The code is designed to be used with either an ANSI C or C++ compiler. + * The structures are designed to allow them to be used with any + * structure alignment upto 8. + **********************************************************************/ + +#include "ocrclass.h" + +#define EUC_FORMAT_MASK 0xe0 + +/********************************************************************** + * ocr_open_shm + * + * Attempt to connect to the shared memory segment and semaphores used + * in talking to the OCR engine. Called from OCR engine. + * The parameters are the command line arguments in order. + * The final parameter is a return value indicating the user-requested + * language. The value will be LANGE_NONE if the user wishes to use + * the default. + **********************************************************************/ +#ifdef __MAC__ +INT16 ocr_open_shm(UINT16 *lang); +#else +INT16 ocr_open_shm( /*open the shm */ + const char *shm_h, /*handle of shm */ + const char *shm_size, /*size of shm segment */ + const char *mutex_h, /*hp mutex */ + const char *ocr_h, /*ocr semaphore */ + const char *hp_h, /*hp semaphore */ + const char *lang_str, /*language */ + UINT16 *lang /*required language */ + ); +#endif + +/********************************************************************** + * ocr_append_fontinfo + * + * Initialize one of the font descriptors. + **********************************************************************/ + +INT16 ocr_append_fontinfo( /*put info into shm */ + UINT16 language, /*default language */ + UINT8 font_family, /*serif/not, fixed/not */ + UINT8 char_set, /*character set standard */ + UINT8 pitch, /*fixed or prop */ + const char *name /*plain ascii name */ + ); + +/********************************************************************** + * ocr_setup_startinfo + * + * Setup the info on the OCR engine. Uses 16 bit chars to name the + * engine. + **********************************************************************/ + +INT16 ocr_setup_startinfo( /*put info into shm */ + UINT32 protocol, /*interface version */ + UINT16 language, /*default language */ + const UINT16 *name, /*name of engine */ + const UINT16 *version /*version of engine */ + ); + +/********************************************************************** + * ocr_setup_startinfo_ansi + * + * Setup the info on the OCR engine. Uses 8 bit chars to name the + * engine. + **********************************************************************/ + +INT16 ocr_setup_startinfo_ansi( /*put info into shm */ + UINT32 protocol, /*interface version */ + UINT16 language, /*default language */ + const char *name, /*name of engine */ + const char *version /*version of engine */ + ); + +/********************************************************************** + * ocr_get_first_image_strip + * + * Wait for the master to send the first image strip and return a + * pointer to it. The result is NULL if it is time to exit. + **********************************************************************/ + + /*get image strip */ +ESTRIP_DESC *ocr_get_first_image_strip(); + +/********************************************************************** + * ocr_get_next_image_strip + * + * Wait for the master to send the next image strip and return a + * pointer to it. The result is NULL if it is time to exit. + **********************************************************************/ + + /*get image strip */ +ESTRIP_DESC *ocr_get_next_image_strip(); + +/********************************************************************** + * ocr_setup_monitor + * + * Setup the progress monitor. Call before starting the recognize task. + **********************************************************************/ + +ETEXT_DESC *ocr_setup_monitor(); /*setup monitor */ + +/********************************************************************** + * ocr_char_space + * + * Return the number of chars that can be fitted into the buffer. + **********************************************************************/ + +INT32 ocr_char_space(); /*put char into shm */ + +/********************************************************************** + * ocr_append_char + * + * Add a character to the output. Returns OKAY if successful, HPERR + * if there was insufficient room in the buffer. + **********************************************************************/ + +INT16 ocr_append_char( /*put char into shm */ + UINT16 char_code, /*character itself */ + INT16 left, /*of char (-1) */ + INT16 right, /*of char (-1) */ + INT16 top, /*of char (-1) */ + INT16 bottom, /*of char (-1) */ + INT16 font_index, /*what font (-1) */ + UINT8 confidence, /*0=perfect, 100=reject (0/100) */ + UINT8 point_size, /*of char, 72=i inch, (10) */ + INT8 blanks, /*no of spaces before this char (1) */ + UINT8 enhancement, /*char enhancement (0) */ + OCR_CHAR_DIRECTION text_dir, /*rendering direction (OCR_CDIR_RIGHT_LEFT) */ + OCR_LINE_DIRECTION line_dir, /*line rendering direction (OCR_LDIR_DOWN_RIGHT) */ + OCR_NEWLINE_TYPE nl_type /*type of newline (if any) (OCR_NL_NONE) */ + ); + +/********************************************************************** + * ocr_send_text + * + * Send the text to the host and wait for the ack. + * Use this function after a sequence of ocr_append_text calls to + * actually sent the text to the master process. + * Set more to come TRUE if there is more text in this page, FALSE + * if the OCR engine is now ready to receive another image. + **********************************************************************/ + +INT16 ocr_send_text( /*send shm */ + BOOL8 more_to_come /*any text left */ + ); + +/********************************************************************** + * ocr_shutdown + * + * Closedown communications with the HP side and free up handles. + **********************************************************************/ + +INT16 ocr_shutdown(); /*closedown */ + +/********************************************************************** + * ocr_error + * + * Inform the HP side of an error. + * The OCR engine should do any cleanup of its own and exit aferwards. + * Uses the current state to determine how to send it and cleanup. + **********************************************************************/ + +void ocr_error( /*send an error code */ + OCR_ERR_CODE code /*error code */ + ); +#endif diff --git a/ccutil/platform.h b/ccutil/platform.h new file mode 100644 index 0000000000..ccdea18525 --- /dev/null +++ b/ccutil/platform.h @@ -0,0 +1,14 @@ +// Place holder +#define DLLSYM +#ifdef __MSW32__ +#define SIGNED +#else +#define __UNIX__ +#include +#ifndef PATH_MAX +#define MAX_PATH 4096 +#else +#define MAX_PATH PATH_MAX +#endif +#define SIGNED signed +#endif diff --git a/ccutil/scanutils.cpp b/ccutil/scanutils.cpp new file mode 100644 index 0000000000..0bc6dbca8a --- /dev/null +++ b/ccutil/scanutils.cpp @@ -0,0 +1,543 @@ +// Copyright 2006 Google Inc. +// All Rights Reserved. +// Author: renn +// +// The fscanf, vfscanf and creat functions are implemented so that their +// functionality is mostly like their stdio counterparts. However, currently +// these functions do not use any buffering, making them rather slow. +// File streams are thus processed one character at a time. +// Although the implementations of the scanf functions do lack a few minor +// features, they should be sufficient for their use in tesseract. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scanutils.h" + +enum Flags { + FL_SPLAT = 0x01, // Drop the value, do not assign + FL_INV = 0x02, // Character-set with inverse + FL_WIDTH = 0x04, // Field width specified + FL_MINUS = 0x08, // Negative number +}; + +enum Ranks { + RANK_CHAR = -2, + RANK_SHORT = -1, + RANK_INT = 0, + RANK_LONG = 1, + RANK_LONGLONG = 2, + RANK_PTR = INT_MAX // Special value used for pointers +}; + +const enum Ranks kMinRank = RANK_CHAR; +const enum Ranks kMaxRank = RANK_LONGLONG; + +const enum Ranks kIntMaxRank = RANK_LONGLONG; +const enum Ranks kSizeTRank = RANK_LONG; +const enum Ranks kPtrDiffRank = RANK_LONG; + +enum Bail { + BAIL_NONE = 0, // No error condition + BAIL_EOF, // Hit EOF + BAIL_ERR // Conversion mismatch +}; + +// Helper functions ------------------------------------------------------------ +inline size_t LongBit() { + return CHAR_BIT * sizeof(long); +} + +static inline int +SkipSpace(FILE *s) +{ + int p; + while (isspace(p = fgetc(s))); + ungetc(p, s); // Make sure next char is available for reading + return p; +} + +static inline void +SetBit(unsigned long *bitmap, unsigned int bit) +{ + bitmap[bit/LongBit()] |= 1UL << (bit%LongBit()); +} + +static inline int +TestBit(unsigned long *bitmap, unsigned int bit) +{ + return static_cast(bitmap[bit/LongBit()] >> (bit%LongBit())) & 1; +} + +static inline int DigitValue(int ch) +{ + if (ch >= '0' && ch <= '9') { + return ch-'0'; + } else if (ch >= 'A' && ch <= 'Z') { + return ch-'A'+10; + } else if (ch >= 'a' && ch <= 'z') { + return ch-'a'+10; + } else { + return -1; + } +} + +// IO (re-)implementations ----------------------------------------------------- +uintmax_t streamtoumax(FILE* s, int base) +{ + int minus = 0; + uintmax_t v = 0; + int d, c = 0; + + for (c = fgetc(s); + isspace(static_cast(c)) && (c != EOF); + c = fgetc(s)) + + // Single optional + or - + if (c == '-' || c == '+') { + minus = (c == '-'); + c = fgetc(s); + } + + // Assign correct base + if (base == 0) { + if (c == '0') { + c = fgetc(s); + if (c == 'x' || c == 'X') { + base = 16; + c = fgetc(s); + } else { + base = 8; + } + } + } else if (base == 16) { + if (c == '0') { + c = fgetc(s); + if (c == 'x' && c == 'X') c = fgetc(s); + } + } + + // Actual number parsing + for (; (c != EOF) && (d = DigitValue(c)) >= 0 && d < base; c = fgetc(s)) + v = v*base + d; + + ungetc(c, s); + return minus ? -v : v; +} + +double streamtofloat(FILE* s) +{ + int minus = 0; + int v = 0; + int d, c = 0; + int k = 1; + int w = 0; + + for (c = fgetc(s); + isspace(static_cast(c)) && (c != EOF); + c = fgetc(s)); + + // Single optional + or - + if (c == '-' || c == '+') { + minus = (c == '-'); + c = fgetc(s); + } + + // Actual number parsing + for (; (c != EOF) && (d = DigitValue(c)) >= 0; c = fgetc(s)) + v = v*10 + d; + if (c == '.') { + for (c = fgetc(s); (c != EOF) && (d = DigitValue(c)) >= 0; c = fgetc(s)) { + w = w*10 + d; + k *= 10; + } + } else if (c == 'e' || c == 'E') + printf("WARNING: Scientific Notation not supported!"); + + ungetc(c, s); + double f = static_cast(v) + + static_cast(w) / static_cast(k); + + return minus ? -f : f; +} + +double strtofloat(const char* s) +{ + int minus = 0; + int v = 0; + int d, c; + int k = 1; + int w = 0; + + while(*s && isspace(static_cast(*s))) s++; + + // Single optional + or - + if (*s == '-' || *s == '+') { + minus = (*s == '-'); + s++; + } + + // Actual number parsing + for (; *s && (d = DigitValue(*s)) >= 0; s++) + v = v*10 + d; + if (*s == '.') { + for (++s; *s && (d = DigitValue(*s)) >= 0; s++) { + w = w*10 + d; + k *= 10; + } + } else if (*s == 'e' || *s == 'E') + printf("WARNING: Scientific Notation not supported!"); + + double f = static_cast(v) + + static_cast(w) / static_cast(k); + + return minus ? -f : f; +} + +int fscanf(FILE* stream, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vfscanf(stream, format, ap); + va_end(ap); + + return rv; +} + +int vfscanf(FILE* stream, const char *format, va_list ap) +{ + const char *p = format; + char ch; + int q = 0; + uintmax_t val = 0; + int rank = RANK_INT; // Default rank + unsigned int width = UINT_MAX; + int base; + int flags = 0; + enum { + ST_NORMAL, // Ground state + ST_FLAGS, // Special flags + ST_WIDTH, // Field width + ST_MODIFIERS, // Length or conversion modifiers + ST_MATCH_INIT, // Initial state of %[ sequence + ST_MATCH, // Main state of %[ sequence + ST_MATCH_RANGE, // After - in a %[ sequence + } state = ST_NORMAL; + char *oarg, *sarg = NULL; // %s %c or %[ string argument + enum Bail bail = BAIL_NONE; + int sign; + int converted = 0; // Successful conversions + unsigned long matchmap[((1 << CHAR_BIT)+(LongBit()-1))/LongBit()]; + int matchinv = 0; // Is match map inverted? + unsigned char range_start = 0; + off_t start_off = ftell(stream); + + // Skip leading spaces + SkipSpace(stream); + + while ((ch = *p++) && !bail) { + switch (state) { + case ST_NORMAL: + if (ch == '%') { + state = ST_FLAGS; + flags = 0; rank = RANK_INT; width = UINT_MAX; + } else if (isspace(static_cast(ch))) { + SkipSpace(stream); + } else { + if (fgetc(stream) != ch) + bail = BAIL_ERR; // Match failure + } + break; + + case ST_FLAGS: + switch (ch) { + case '*': + flags |= FL_SPLAT; + break; + + case '0' ... '9': + width = (ch-'0'); + state = ST_WIDTH; + flags |= FL_WIDTH; + break; + + default: + state = ST_MODIFIERS; + p--; // Process this character again + break; + } + break; + + case ST_WIDTH: + if (ch >= '0' && ch <= '9') { + width = width*10+(ch-'0'); + } else { + state = ST_MODIFIERS; + p--; // Process this character again + } + break; + + case ST_MODIFIERS: + switch (ch) { + // Length modifiers - nonterminal sequences + case 'h': + rank--; // Shorter rank + break; + case 'l': + rank++; // Longer rank + break; + case 'j': + rank = kIntMaxRank; + break; + case 'z': + rank = kSizeTRank; + break; + case 't': + rank = kPtrDiffRank; + break; + case 'L': + case 'q': + rank = RANK_LONGLONG; // long double/long long + break; + + default: + // Output modifiers - terminal sequences + state = ST_NORMAL; // Next state will be normal + if (rank < kMinRank) // Canonicalize rank + rank = kMinRank; + else if (rank > kMaxRank) + rank = kMaxRank; + + switch (ch) { + case 'P': // Upper case pointer + case 'p': // Pointer + rank = RANK_PTR; + base = 0; sign = 0; + goto scan_int; + + case 'i': // Base-independent integer + base = 0; sign = 1; + goto scan_int; + + case 'd': // Decimal integer + base = 10; sign = 1; + goto scan_int; + + case 'o': // Octal integer + base = 8; sign = 0; + goto scan_int; + + case 'u': // Unsigned decimal integer + base = 10; sign = 0; + goto scan_int; + + case 'x': // Hexadecimal integer + case 'X': + base = 16; sign = 0; + goto scan_int; + + case 'n': // Number of characters consumed + val = ftell(stream) - start_off; + goto set_integer; + + scan_int: + q = SkipSpace(stream); + if ( q <= 0 ) { + bail = BAIL_EOF; + break; + } + val = streamtoumax(stream, base); + converted++; + // fall through + + set_integer: + if (!(flags & FL_SPLAT)) { + switch(rank) { + case RANK_CHAR: + *va_arg(ap, unsigned char *) + = static_cast(val); + break; + case RANK_SHORT: + *va_arg(ap, unsigned short *) + = static_cast(val); + break; + case RANK_INT: + *va_arg(ap, unsigned int *) + = static_cast(val); + break; + case RANK_LONG: + *va_arg(ap, unsigned long *) + = static_cast(val); + break; + case RANK_LONGLONG: + *va_arg(ap, unsigned long long *) + = static_cast(val); + break; + case RANK_PTR: + *va_arg(ap, void **) + = reinterpret_cast(static_cast(val)); + break; + } + } + break; + + case 'f': // Preliminary float value parsing + case 'g': + case 'G': + case 'e': + case 'E': + q = SkipSpace(stream); + if (q <= 0) { + bail = BAIL_EOF; + break; + } + + double fval = streamtofloat(stream); + switch(rank) { + case RANK_INT: + *va_arg(ap, float *) = static_cast(fval); + break; + case RANK_LONG: + *va_arg(ap, double *) = static_cast(fval); + break; + } + converted++; + break; + + case 'c': // Character + width = (flags & FL_WIDTH) ? width : 1; // Default width == 1 + sarg = va_arg(ap, char *); + while (width--) { + if ((q = fgetc(stream)) <= 0) { + bail = BAIL_EOF; + break; + } + *sarg++ = q; + } + if (!bail) + converted++; + break; + + case 's': // String + { + char *sp; + sp = sarg = va_arg(ap, char *); + while (width--) { + q = fgetc(stream); + if (isspace(static_cast(q)) || q <= 0) { + ungetc(q, stream); + break; + } + *sp++ = q; + } + if (sarg != sp) { + *sp = '\0'; // Terminate output + converted++; + } else { + bail = BAIL_EOF; + } + } + break; + + case '[': // Character range + sarg = va_arg(ap, char *); + state = ST_MATCH_INIT; + matchinv = 0; + memset(matchmap, 0, sizeof matchmap); + break; + + case '%': // %% sequence + if (fgetc(stream) != '%' ) + bail = BAIL_ERR; + break; + + default: // Anything else + bail = BAIL_ERR; // Unknown sequence + break; + } + } + break; + + case ST_MATCH_INIT: // Initial state for %[ match + if (ch == '^' && !(flags & FL_INV)) { + matchinv = 1; + } else { + SetBit(matchmap, static_cast(ch)); + state = ST_MATCH; + } + break; + + case ST_MATCH: // Main state for %[ match + if (ch == ']') { + goto match_run; + } else if (ch == '-') { + range_start = static_cast(ch); + state = ST_MATCH_RANGE; + } else { + SetBit(matchmap, static_cast(ch)); + } + break; + + case ST_MATCH_RANGE: // %[ match after - + if (ch == ']') { + SetBit(matchmap, static_cast('-')); + goto match_run; + } else { + int i; + for (i = range_start ; i < (static_cast(ch)) ; i++) + SetBit(matchmap, i); + state = ST_MATCH; + } + break; + + match_run: // Match expression finished + char* oarg = sarg; + while (width) { + q = fgetc(stream); + unsigned char qc = static_cast(q); + if (q <= 0 || !(TestBit(matchmap, qc)^matchinv)) { + ungetc(q, stream); + break; + } + *sarg++ = q; + } + if (oarg != sarg) { + *sarg = '\0'; + converted++; + } else { + bail = (q <= 0) ? BAIL_EOF : BAIL_ERR; + } + break; + } + } + + if (bail == BAIL_EOF && !converted) + converted = -1; // Return EOF (-1) + + return converted; +} + +int creat(const char *pathname, mode_t mode) +{ + return open(pathname, O_CREAT | O_TRUNC | O_WRONLY, mode); +} diff --git a/ccutil/scanutils.h b/ccutil/scanutils.h new file mode 100644 index 0000000000..fcfea8ade0 --- /dev/null +++ b/ccutil/scanutils.h @@ -0,0 +1,55 @@ +// Copyright 2006 Google Inc. +// All Rights Reserved. +// Author: renn +// +// Contains file io functions (mainly for file parsing), that might not be +// available, on embedded devices, or that have an incomplete implementation +// there. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SCANUTILS_H +#define SCANUTILS_H + +#ifdef EMBEDDED + +#include +#include +#include +#include + +// Attempts to parse the given file stream s as an integer of the base +// 'base'. Returns the first successfully parsed integer as a uintmax_t, or +// 0, if none was found. +uintmax_t streamtoumax(FILE* s, int base); + +// Parse a file stream according to the given format. See the fscanf manpage +// for more information, as this function attempts to mimic its behavior. +// Note that scientific loating-point notation is not supported. +int fscanf(FILE* stream, const char *format, ...); + +// Parse a file stream according to the given format. See the fscanf manpage +// for more information, as this function attempts to mimic its behavior. +// Note that scientific loating-point notation is not supported. +int vfscanf(FILE* stream, const char *format, va_list ap); + +// Create a file at the specified path. See the creat manpage for more +// information, as this function attempts to mimic its behavior. +int creat(const char *pathname, mode_t mode); + +// Convert the specified C-String to a float. Returns the first parsed float, +// or 0.0 if no floating point value could be found. Note that scientific +// floating-point notation is not supported. +double strtofloat(const char* s); + +#endif + +#endif diff --git a/ccutil/secname.h b/ccutil/secname.h new file mode 100644 index 0000000000..5ca4e1982d --- /dev/null +++ b/ccutil/secname.h @@ -0,0 +1,9 @@ +/* Include this file in any module which needs to have conditional compilation + of sensitive code for UNLV. In INTERNAL mode SECURE_NAMES is NOT defined. + For UNLV mode it IS defined, allowing multiple modules to do conditional + compilation on the same name. +*/ + +#ifndef SECURE_NAMES +/* #define SECURE_NAMES */ +#endif diff --git a/ccutil/serialis.cpp b/ccutil/serialis.cpp new file mode 100644 index 0000000000..9e31a961fd --- /dev/null +++ b/ccutil/serialis.cpp @@ -0,0 +1,112 @@ +/********************************************************************** + * File: serialis.h (Formerly serialmac.h) + * Description: Inline routines and macros for serialisation functions + * Author: Phil Cheatle + * Created: Tue Oct 08 08:33:12 BST 1991 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include "serialis.h" +#include "scanutils.h" + +/* ************************************************************************** + +These are the only routines that write/read data to/from the serialisation. + +"serialise_bytes" and "de_serialise_bytes" are used to serialise NON class +items. The "make_serialise" macro generates "serialise" and "de_serialise" +member functions for the class name specified in the macro parameter. + +************************************************************************** */ + +DLLSYM void *de_serialise_bytes(FILE *f, int size) { + void *ptr; + + ptr = alloc_mem (size); + /* + printf( "De_serialising bytes\n" ); + printf( " Addr: %d Size: %d\n", int(ptr), size ); + */ + if (fread (ptr, size, 1, f) != 1) + READFAILED.error ("de_serialise_bytes", ABORT, NULL); + return ptr; +} + + +DLLSYM void serialise_bytes(FILE *f, void *ptr, int size) { + /* + printf( "Serialising bytes\n" ); + printf( " Addr: %d Size: %d\n", int(ptr), size ); + */ + if (fwrite (ptr, size, 1, f) != 1) + WRITEFAILED.error ("serialise_bytes", ABORT, NULL); +} + + +DLLSYM void serialise_INT32(FILE *f, INT32 the_int) { + if (fprintf (f, INT32FORMAT "\n", the_int) < 0) + WRITEFAILED.error ("serialise_INT32", ABORT, NULL); +} + + +DLLSYM INT32 de_serialise_INT32(FILE *f) { + INT32 the_int; + + if (fscanf (f, INT32FORMAT, &the_int) != 1) + READFAILED.error ("de_serialise_INT32", ABORT, NULL); + return the_int; +} + + +DLLSYM void serialise_FLOAT64(FILE *f, double the_float) { + if (fprintf (f, "%g\n", the_float) < 0) + WRITEFAILED.error ("serialise_FLOAT64", ABORT, NULL); +} + + +DLLSYM double de_serialise_FLOAT64(FILE *f) { + double the_float; + + if (fscanf (f, "%lg", &the_float) != 1) + READFAILED.error ("de_serialise_FLOAT64", ABORT, NULL); + return the_float; +} + + +/********************************************************************** + * reverse32 + * + * Byte swap an INT32 or UINT32. + **********************************************************************/ + +DLLSYM UINT32 reverse32( //switch endian + UINT32 num //number to fix + ) { + return (reverse16 ((UINT16) (num & 0xffff)) << 16) + | reverse16 ((UINT16) ((num >> 16) & 0xffff)); +} + + +/********************************************************************** + * reverse16 + * + * Byte swap an INT16 or UINT16. + **********************************************************************/ + +DLLSYM UINT16 reverse16( //switch endian + UINT16 num //number to fix + ) { + return ((num & 0xff) << 8) | ((num >> 8) & 0xff); +} diff --git a/ccutil/serialis.h b/ccutil/serialis.h new file mode 100644 index 0000000000..15483deebf --- /dev/null +++ b/ccutil/serialis.h @@ -0,0 +1,95 @@ +/********************************************************************** + * File: serialis.h (Formerly serialmac.h) + * Description: Inline routines and macros for serialisation functions + * Author: Phil Cheatle + * Created: Tue Oct 08 08:33:12 BST 1991 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef SERIALIS_H +#define SERIALIS_H + +#include +#include +#include +#include "memry.h" +#include "errcode.h" +#include "fileerr.h" + +/* ************************************************************************** + +These are the only routines that write/read data to/from the serialisation. + +"serialise_bytes" and "de_serialise_bytes" are used to serialise NON class +items. The "make_serialise" macro generates "serialise" and "de_serialise" +member functions for the class name specified in the macro parameter. + +************************************************************************** */ + +extern DLLSYM void *de_serialise_bytes(FILE *f, int size); +extern DLLSYM void serialise_bytes(FILE *f, void *ptr, int size); +extern DLLSYM void serialise_INT32(FILE *f, INT32 the_int); +extern DLLSYM INT32 de_serialise_INT32(FILE *f); +extern DLLSYM void serialise_FLOAT64(FILE *f, double the_float); +extern DLLSYM double de_serialise_FLOAT64(FILE *f); +extern DLLSYM UINT32 reverse32( //switch endian + UINT32 num //number to fix + ); +extern DLLSYM UINT16 reverse16( //switch endian + UINT16 num //number to fix + ); + +/*********************************************************************** + QUOTE_IT MACRO DEFINITION + =========================== +Replace with "". may be an arbitrary number of tokens +***********************************************************************/ + +#define QUOTE_IT( parm ) #parm + +#define make_serialise( CLASSNAME ) \ + \ + NEWDELETE2(CLASSNAME) \ + \ +void serialise( \ + FILE* f) \ +{ \ + CLASSNAME* shallow_copy; \ + \ + shallow_copy = (CLASSNAME*) alloc_struct( sizeof( *this ) ); \ + memmove( shallow_copy, this, sizeof( *this ) ); \ + \ + shallow_copy->prep_serialise(); \ + if (fwrite( (char*) shallow_copy, sizeof( *shallow_copy ), 1, f ) != 1)\ + WRITEFAILED.error( QUOTE_IT( CLASSNAME::serialise ), \ + ABORT, NULL ); \ + \ + free_struct( shallow_copy, sizeof( *this ) ); \ + this->dump( f ); \ +} \ + \ + static CLASSNAME* de_serialise( \ + FILE* f) \ +{ \ + CLASSNAME* restored; \ + \ + restored = (CLASSNAME*) alloc_struct( sizeof( CLASSNAME ) ); \ + if (fread( (char*) restored, sizeof( CLASSNAME ), 1, f ) != 1) \ + READFAILED.error( QUOTE_IT( CLASSNAME::de_serialise ), \ + ABORT, NULL ); \ + \ + restored->de_dump( f ); \ + return restored; \ +} +#endif diff --git a/ccutil/stderr.h b/ccutil/stderr.h new file mode 100644 index 0000000000..b5e96fbe7c --- /dev/null +++ b/ccutil/stderr.h @@ -0,0 +1,26 @@ +/********************************************************************** + * File: stderr.h (Formerly stderrs.h) + * Description: File defining error messages fundamental of commonly used. + * Author: Ray Smith + * Created: Fri Aug 10 15:23:14 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef STDERR_H +#define STDERR_H + +#include "errcode.h" + +const ERRCODE MEMORY_OUT = "Out of memory"; +#endif diff --git a/ccutil/strngs.cpp b/ccutil/strngs.cpp new file mode 100644 index 0000000000..4f85215b33 --- /dev/null +++ b/ccutil/strngs.cpp @@ -0,0 +1,209 @@ +/********************************************************************** + * File: strngs.c (Formerly strings.c) + * Description: STRING class functions. + * Author: Ray Smith + * Created: Fri Feb 15 09:13:30 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include "tprintf.h" +#include "strngs.h" + +/********************************************************************** + * STRING::operator= + * + * Assign a char* to a STRING. + **********************************************************************/ + +STRING & STRING::operator= ( //assign char* +const char *string //string to copy +) { + if (string != NULL) { + INT32 + length = strlen (string) + 1;//length of source + + if (ptr == NULL) + //get space + ptr = alloc_string (length); + //got wrong size + else if (strlen (ptr) != (UINT32) length - 1) { + free_string(ptr); //free old space + //get new space + ptr = alloc_string (length); + } + if (ptr == NULL) { + tprintf ("No memory to allocate string"); + return *this; + } + strcpy(ptr, string); //copy string + } + else { + if (ptr == NULL) + ptr = alloc_string (1); //get space + //got wrong size + else if (strlen (ptr) != 0) { + free_string(ptr); //free old space + ptr = alloc_string (1); //get new space + } + if (ptr == NULL) { + tprintf ("No memory to allocate string"); + return *this; + } + *ptr = '\0'; //copy string + } + + return *this; +} + + +/********************************************************************** + * STRING::operator+ + * + * Concatenate 2 STRINGs. + **********************************************************************/ + +STRING +STRING::operator+ ( //concatenation +const STRING & string //second string +) const +{ + INT32 length; //length of 1st op + STRING result; //concatenated string + + if (ptr == NULL) + length = 0; + else + length = strlen (ptr); + result.ptr = alloc_string (length + strlen (string.ptr) + 1); + //total length + if (result.ptr == NULL) { + tprintf ("No memory to allocate string"); + return result; + } + result.ptr[0] = '\0'; + if (ptr != NULL) + strcpy (result.ptr, ptr); + if (string.ptr != NULL) + //put together + strcpy (result.ptr + length, string.ptr); + return result; +} + + +/********************************************************************** + * STRING::operator+ + * + * Concatenate char to STRING. + **********************************************************************/ + +STRING +STRING::operator+ ( //concatenation +const char ch //char +) const +{ + INT32 length; //length of 1st op + STRING result; //concatenated string + + if (ptr == NULL) + length = 0; + else + length = strlen (ptr); + //total length + result.ptr = alloc_string (length + 2); + if (result.ptr == NULL) { + tprintf ("No memory to allocate string"); + return result; + } + if (ptr != NULL) + strcpy (result.ptr, ptr); + result.ptr[length] = ch; //put together + result.ptr[length + 1] = '\0'; + return result; +} + + +/********************************************************************** + * STRING::operator+= + * + * Concatenate 2 strings putting the result straing back in the first + **********************************************************************/ + +STRING & STRING::operator+= ( //inplace cat +const char *string //string to add +) { + INT32 + length; //length of 1st op + char * + src; //source string + + if (string == NULL || string[0] == '\0') + return *this; //unchanged + if (ptr == NULL) + length = 0; + else + length = strlen (ptr); //length of 1st op + src = ptr; //temp copy + //new length + ptr = alloc_string (length + strlen (string) + 1); + if (ptr == NULL) { + tprintf ("No memory to allocate string"); + ptr = src; + return *this; + } + if (src != NULL) { + strcpy(ptr, src); //copy old + free_string(src); //free old one + } + strcpy (ptr + length, string); //add new + return *this; +} + + +/********************************************************************** + * STRING::operatot+= + * + * Concatenate a char t a string putting the result straing back in the string + **********************************************************************/ + +STRING & STRING::operator+= ( //inplace cat +const char ch //char to add +) { + INT32 + length; //length of 1st op + char * + src; //source string + + if (ch == '\0') + return *this; //unchanged + if (ptr == NULL) + length = 0; + else + length = strlen (ptr); //length of 1st op + src = ptr; //temp copy + //new length + ptr = alloc_string (length + 2); + if (ptr == NULL) { + tprintf ("No memory to allocate string"); + ptr = src; + return *this; + } + if (src != NULL) { + strcpy(ptr, src); //copy old + free_string(src); //free old one + } + ptr[length] = ch; //add new char + ptr[length + 1] = '\0'; + return *this; +} diff --git a/ccutil/strngs.h b/ccutil/strngs.h new file mode 100644 index 0000000000..37f918092d --- /dev/null +++ b/ccutil/strngs.h @@ -0,0 +1,178 @@ +/********************************************************************** + * File: strngs.h (Formerly strings.h) + * Description: STRING class definition. + * Author: Ray Smith + * Created: Fri Feb 15 09:15:01 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef STRNGS_H +#define STRNGS_H + +#include +#include "memry.h" +#include "serialis.h" + +class DLLSYM STRING +{ + char *ptr; //ptr to the chars + + public: + STRING() { //constructor + ptr = NULL; //empty string + } + + STRING( //classwise copy + const STRING &string) { + if (string.ptr != NULL) { + //length of source + INT32 length = strlen (string.ptr) + 1; + + //get space + ptr = alloc_string (length); + strcpy (ptr, string.ptr);//and copy it + } + else { + ptr = alloc_string (1); + if (ptr != NULL) + *ptr = '\0'; + } + } + + STRING( //contruct from char* + const char *string) { + if (string != NULL) { + //length of source + INT32 length = strlen (string) + 1; + + //get space + ptr = alloc_string (length); + if (ptr != NULL) + strcpy(ptr, string); //and copy it + } + else { + ptr = alloc_string (1); + if (ptr != NULL) + *ptr = '\0'; + } + } + + ~STRING () { //destructor + if (ptr != NULL) + free_string(ptr); //give it back + } + + char &operator[] ( //access function + INT32 index) const //string index + { + return ptr[index]; //no bounds checks + } + + BOOL8 contains( //char in string? + const char c) const { + if ((ptr == NULL) || ((c != '\0') && strchr (ptr, c) == NULL)) + return FALSE; + else + return TRUE; + } + + INT32 length() const { //string length + if (ptr != NULL) + return strlen (ptr); + else + return 0; + } + + const char *string() const { //ptr to string + return ptr; + } + + BOOL8 operator== ( //string equality + const STRING & string) const + { + if (ptr != NULL && string.ptr != NULL) + return strcmp (ptr, string.ptr) == 0; + else + return (ptr == NULL || *ptr == '\0') + && (string.ptr == NULL || *(string.ptr) == '\0'); + } + + BOOL8 operator!= ( //string equality + const STRING & string) const + { + if (ptr != NULL && string.ptr != NULL) + return strcmp (ptr, string.ptr) != 0; + else + return !((ptr == NULL || *ptr == '\0') + && (string.ptr == NULL || *(string.ptr) == '\0')); + } + + BOOL8 operator!= ( //string equality + const char *string) const + { + if (ptr != NULL && string != NULL) + return strcmp (ptr, string) != 0; + else + return !((ptr == NULL || *ptr == '\0') + && (string == NULL || *string == '\0')); + } + + STRING & operator= ( //assignment + const char *string); //of char* + + STRING & operator= ( //assignment + const STRING & string) { //of string + *this = string.ptr; //as for char* + return *this; + } + + STRING operator+ ( //concatenation + const STRING & string) const; + + STRING operator+ ( //char concatenation + const char ch) const; + + STRING & operator+= ( //inplace cat + const char *string); + STRING & operator+= ( //inplace cat + const STRING & string) { + *this += string.ptr; + return *this; + } + + STRING & operator+= ( //inplace char cat + const char ch); + + void prep_serialise() { //set ptrs to counts + ptr = (char *) (length () + 1); + } + + void dump( //write external bits + FILE *f) { + serialise_bytes (f, (void *) ptr, (int) (length () + 1)); + } + + void de_dump( //read external bits + FILE *f) { + char *instring; //input from read + + instring = (char *) de_serialise_bytes (f, (ptrdiff_t) ptr); + ptr = NULL; + *this = instring; + free_mem(instring); + } + + make_serialise (STRING) +}; +#endif diff --git a/ccutil/tessclas.h b/ccutil/tessclas.h new file mode 100644 index 0000000000..2475fd043e --- /dev/null +++ b/ccutil/tessclas.h @@ -0,0 +1,135 @@ +#ifndef TESSCLAS_H +#define TESSCLAS_H 1 + +#define SPLINESIZE 23 /*max spline parts to a line */ + +#define TBLOBFLAGS 4 /*No of flags in a blob */ +#define MAX_WO_CLASSES 3 +#define EDGEPTFLAGS 4 /*concavity,length etc. */ + +typedef struct +{ + double a; /*x squared */ + double b; /*x */ + double c; /*constant */ +} QUAD_SPEC; /*definiton of quadratic */ + +typedef struct +{ + int segments; /*no of spline segments */ + int xstarts[SPLINESIZE]; /*start x coords */ + QUAD_SPEC quads[SPLINESIZE]; /*quadratic sections */ +} SPLINE_SPEC; /*quadratic spline */ + +typedef struct +{ + short x; /*absolute x coord */ + short y; /*absolute y coord */ +} TPOINT; +typedef TPOINT VECTOR; /*structure for coordinates */ + +typedef struct +{ + char dx; /*compact vectors */ + char dy; +} BYTEVEC; + +typedef struct edgeptstruct +{ + TPOINT pos; /*position */ + VECTOR vec; /*vector to next point */ + char flags[EDGEPTFLAGS]; /*concavity, length etc */ + struct edgeptstruct *next; /*anticlockwise element */ + struct edgeptstruct *prev; /*clockwise element */ +} EDGEPT; /*point on expanded outline */ + +typedef struct blobstruct +{ + struct olinestruct *outlines; /*list of outlines in blob */ + char flags[TBLOBFLAGS]; /*blob flags */ + char correct; /*correct text */ + char guess; /*best guess */ + /*quickie classification */ + unsigned char classes[MAX_WO_CLASSES]; + /*quickie ratings */ + unsigned char values[MAX_WO_CLASSES]; + struct blobstruct *next; /*next blob in block */ +} TBLOB; /*blob structure */ + +typedef struct olinestruct +{ + TPOINT topleft; /*top left of loop */ + TPOINT botright; /*bottom right of loop */ + TPOINT start; /*start of loop */ + BYTEVEC *compactloop; /*ptr to compacted loop */ + EDGEPT *loop; /*edgeloop */ + void *node; /*1st node on outline */ + struct olinestruct *next; /*next at this level */ + struct olinestruct *child; /*inner outline */ +} TESSLINE; /*outline structure */ + +typedef struct wordstruct +{ + struct textrowstruct *row; /*row it came from */ + char *correct; /*correct word string */ + char *guess; /*guess word string */ + TBLOB *blobs; /*blobs in word */ + int blanks; /*blanks before word */ + int blobcount; /*no of blobs in word */ + struct wordstruct *next; /*next word */ +} TWERD; /*word structure */ + +typedef struct textrowstruct +{ + int blobcount; /** count of blobs in row. **/ + TBLOB *blobs; /*list of blobs in row */ + TWERD *words; /*list of words in row */ + int mean_y; /** y coordinate of centre of row **/ + int max_y; /** y coordinate of top of row **/ + int min_y; /** y coordinate of bottom of row **/ + SPLINE_SPEC xheight; /*top of row */ + SPLINE_SPEC baseline; /*bottom of row */ + float descdrop; /*descender drop */ + float ascrise; /*ascender rise */ + float lineheight; /*average xheight-baseline */ + int kerning; /*kerning of row */ + int space; /*spacing of row */ + float space_threshold; /*Bayesian space limit */ + int p_spaced; /*proportinal flag */ + int b_space; /*block spacing */ + int b_kern; /*block kerning */ + struct textrowstruct *next; /*next row in block */ +} TEXTROW; + +typedef struct blockstruct /** list of coordinates **/ +{ + TBLOB *blobs; /*blobs in block */ + TEXTROW *rows; /*rows in block */ + int blobcount; /*no of blobs */ + short xmin; + short xmax; + short ymin; + short ymax; + char type; /** block type **/ + char p_spaced; /** flag to show propertianal spacing **/ + short rowcount; /** number of rows **/ + short leading; /** space between rows **/ + short kerning; /** space between characters **/ + short space; /** distance between char centres **/ + short minwidth; /*min width of char in block */ + short p_size; /** point size of text **/ + short l_margin; /** posn of left margin **/ + short italic; /** flag to show italic block **/ + short spurious; /** percentage of spurious characters **/ + struct blockstruct *next; /*next text block */ +} TEXTBLOCK; /*block from image */ + +/********************************************************************** + * iterate_blobs + * + * Visit all the words in a list using a local variable. + **********************************************************************/ + +#define iterate_blobs(blob,blobs) \ +for (blob = blobs; blob != NULL; blob = blob->next) +#endif diff --git a/ccutil/tprintf.cpp b/ccutil/tprintf.cpp new file mode 100644 index 0000000000..4d6aaf0851 --- /dev/null +++ b/ccutil/tprintf.cpp @@ -0,0 +1,136 @@ +/********************************************************************** + * File: tprintf.c + * Description: Trace version of printf - portable between UX and NT + * Author: Phil Cheatle + * Created: Wed Jun 28 15:01:15 BST 1995 + * + * (C) Copyright 1995, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#include "mfcpch.h" //precompiled headers +#include +#include +#include "strngs.h" +#include "varable.h" +#include "debugwin.h" +//#include "ipeerr.h" +#include "tprintf.h" + +#define MAX_MSG_LEN 1024 + +#define EXTERN +#ifdef __MSW32__ +DLLSYM STRING_VAR (debug_file, "tesseract.log", "File to send tprintf output to"); +#else +DLLSYM STRING_VAR (debug_file, "", "File to send tprintf output to"); +#endif +DLLSYM BOOL_VAR (debug_window_on, FALSE, +"Send tprintf to window unless file set"); + +DLLSYM void +tprintf ( //Trace printf +const char *format, ... //special message +) { + va_list args; //variable args + static FILE *debugfp = NULL; //debug file + //debug window + static DEBUG_WIN *debugwin = NULL; + INT32 offset = 0; //into message + static char msg[MAX_MSG_LEN + 1]; + + va_start(args, format); //variable list + #ifdef __MSW32__ + //Format into msg + offset += _vsnprintf (msg + offset, MAX_MSG_LEN - offset, format, args); + #else + //Format into msg + offset += vsprintf (msg + offset, format, args); + #endif + va_end(args); + + if (debugfp == NULL && strlen (debug_file.string ()) > 0) + debugfp = fopen (debug_file.string (), "w"); + else if (debugfp != NULL && strlen (debug_file.string ()) == 0) { + fclose(debugfp); + debugfp = NULL; + } + if (debugfp != NULL) + fprintf (debugfp, "%s", msg); + else { + + if (debug_window_on) { + if (debugwin == NULL) + //in pixels + debugwin = new DEBUG_WIN ("Debug Window", DEBUG_WIN_XPOS, DEBUG_WIN_YPOS, + //in pixels + DEBUG_WIN_XSIZE, DEBUG_WIN_YSIZE, + debug_lines); + debugwin->dprintf (msg); + } + else { + #ifdef __UNIX__ + // output to stderr - like it used to + fprintf (stderr, "%s", msg); + #endif + + #ifdef __MSW32__ + TRACE ("%s", msg); //Visual C++2.0 macro + #endif + #ifdef __MAC__ + printf ("%s", msg); //Visual C++2.0 macro + #endif + } + } +} + + +/************************************************************************* + * pause_continue() + * UI for a debugging pause - to see an intermediate state + * Returns TRUE to continue as normal to the next pause in the current mode; + * FALSE to quit the current pausing mode. + *************************************************************************/ + +DLLSYM BOOL8 + //special message +pause_continue (const char *format, ... +) { + va_list args; //variable args + char msg[1000]; + STRING str = STRING ("DEBUG PAUSE:\n"); + + va_start(args, format); //variable list + vsprintf(msg, format, args); //Format into msg + va_end(args); + + #ifdef GRAPHICS_DISABLED + // No interaction allowed -> simply go on + return true; + #else + + #ifdef __UNIX__ + printf ("%s\n", msg); + printf ("Type \"c\" to cancel, anything else to continue: "); + char c = getchar (); + return (c != 'c'); + #endif + + #ifdef __MSW32__ + str += + STRING (msg) + STRING ("\nUse OK to continue, CANCEL to stop pausing"); + // return AfxMessageBox( str.string(), MB_OKCANCEL ) == IDOK; + return::MessageBox (NULL, msg, "IMGAPP", + MB_APPLMODAL | MB_OKCANCEL) == IDOK; + #endif + + #endif +} diff --git a/ccutil/tprintf.h b/ccutil/tprintf.h new file mode 100644 index 0000000000..81a9783c31 --- /dev/null +++ b/ccutil/tprintf.h @@ -0,0 +1,35 @@ +/********************************************************************** + * File: tprintf.c + * Description: Trace version of printf - portable between UX and NT + * Author: Phil Cheatle + * Created: Wed Jun 28 15:01:15 BST 1995 + * + * (C) Copyright 1995, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TPRINTF_H +#define TPRINTF_H + +#include "varable.h" + +extern DLLSYM STRING_VAR_H (debug_file, "", "File to send tprintf output to"); +extern DLLSYM BOOL_VAR_H (debug_window_on, TRUE, +"Send tprintf to window unless file set"); + +DLLSYM void tprintf ( //Trace printf +const char *format, ... //special message +); + //special message +DLLSYM BOOL8 pause_continue (const char *format, ... +); +#endif diff --git a/ccutil/unichar.cpp b/ccutil/unichar.cpp new file mode 100644 index 0000000000..6450e08926 --- /dev/null +++ b/ccutil/unichar.cpp @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////// +// File: unichar.cpp +// Description: Unicode character/ligature class. +// Author: Ray Smith +// Created: Wed Jun 28 17:05:01 PDT 2006 +// +// (C) Copyright 2006, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include "unichar.h" + +#define UNI_MAX_LEGAL_UTF32 0x0010FFFF + +// Construct from a utf8 string. If len<0 then the string is null terminated. +// If the string is too long to fit in the UNICHAR then it takes only what +// will fit. Checks for illegal input and stops at an illegal sequence. +// The resulting UNICHAR may be empty. +UNICHAR::UNICHAR(const char* utf8_str, int len) { + int total_len = 0; + int step = 0; + if (len < 0) { + for (len = 0; utf8_str[len] != 0 && len < UNICHAR_LEN; ++len); + } + for (total_len = 0; total_len < len; total_len += step) { + step = utf8_step(utf8_str + total_len); + if (total_len + step > UNICHAR_LEN) + break; // Too long. + if (step == 0) + break; // Illegal first byte. + int i; + for (i = 1; i < step; ++i) + if ((utf8_str[total_len + i] & 0xc0) != 0x80) + break; + if (i < step) + break; // Illegal surrogate + } + memcpy(chars, utf8_str, total_len); + if (total_len < UNICHAR_LEN) { + chars[UNICHAR_LEN - 1] = total_len; + while (total_len < UNICHAR_LEN - 1) + chars[total_len++] = 0; + } +} + +// Construct from a single UCS4 character. Illegal values are ignored, +// resulting in an empty UNICHAR. +UNICHAR::UNICHAR(int unicode) { + const int bytemask = 0xBF; + const int bytemark = 0x80; + + if (unicode < 0x80) { + chars[UNICHAR_LEN - 1] = 1; + chars[2] = 0; + chars[1] = 0; + chars[0] = static_cast(unicode); + } else if (unicode < 0x800) { + chars[UNICHAR_LEN - 1] = 2; + chars[2] = 0; + chars[1] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[0] = static_cast(unicode | 0xc0); + } else if (unicode < 0x10000) { + chars[UNICHAR_LEN - 1] = 3; + chars[2] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[1] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[0] = static_cast(unicode | 0xe0); + } else if (unicode <= UNI_MAX_LEGAL_UTF32) { + chars[UNICHAR_LEN - 1] = 4; + chars[3] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[2] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[1] = static_cast((unicode | bytemark) & bytemask); + unicode >>= 6; + chars[0] = static_cast(unicode | 0xf0); + } else { + memset(chars, 0, UNICHAR_LEN); + } +} + +// Get the first character as UCS-4. +int UNICHAR::first_uni() const { + static const int utf8_offsets[5] = { + 0, 0, 0x3080, 0xE2080, 0x3C82080 + }; + int uni = 0; + int len = utf8_step(chars); + const char* src = chars; + + switch (len) { + default: + break; + case 4: + uni += *src++; + uni <<= 6; + case 3: + uni += *src++; + uni <<= 6; + case 2: + uni += *src++; + uni <<= 6; + case 1: + uni += *src++; + } + uni -= utf8_offsets[len]; + return uni; +} + +// Get a terminated UTF8 string: Must delete[] it after use. +char* UNICHAR::utf8_str() const { + int len = utf8_len(); + char* str = new char[len + 1]; + memcpy(str, chars, len); + str[len] = 0; + return str; +} + +// Get the number of bytes in the first character of the given utf8 string. +int UNICHAR::utf8_step(const char* utf8_str) { + static const char utf8_bytes[256] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0 + }; + + return utf8_bytes[*utf8_str]; +} diff --git a/ccutil/unichar.h b/ccutil/unichar.h new file mode 100644 index 0000000000..b73d401616 --- /dev/null +++ b/ccutil/unichar.h @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////// +// File: unichar.h +// Description: Unicode character/ligature class. +// Author: Ray Smith +// Created: Wed Jun 28 17:05:01 PDT 2006 +// +// (C) Copyright 2006, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef THIRD_PARTY_TESSERACT_CCUTIL_UNICHAR_H__ +#define THIRD_PARTY_TESSERACT_CCUTIL_UNICHAR_H__ + +#include + +// Maximum number of characters that can be stored in a UNICHAR. Must be +// at least 4. Must not exceed 31 without changing the coding of length. +#define UNICHAR_LEN 4 + +// The UNICHAR class holds a single classification result. This may be +// a single Unicode character (stored as between 1 and 4 utf8 bytes) or +// multple Unicode characters representing the NFKC expansion of a ligature +// such as fi, ffl etc. These are also stored as utf8. +class UNICHAR { + public: + UNICHAR() { + memset(chars, 0, UNICHAR_LEN); + } + + // Construct from a utf8 string. If len<0 then the string is null terminated. + // If the string is too long to fit in the UNICHAR then it takes only what + // will fit. + UNICHAR(const char* utf8_str, int len); + + // Construct from a single UCS4 character. + explicit UNICHAR(int unicode); + + // Default copy constructor and operator= are OK. + + // Get the first character as UCS-4. + int first_uni() const; + + // Get the length of the UTF8 string. + int utf8_len() const { + int len = chars[UNICHAR_LEN - 1]; + return len >=0 && len < UNICHAR_LEN ? len : UNICHAR_LEN; + } + + // Get a UTF8 string, but NOT NULL terminated. + const char* utf8() const { + return chars; + } + + // Get a terminated UTF8 string: Must delete[] it after use. + char* utf8_str() const; + + // Get the number of bytes in the first character of the given utf8 string. + static int utf8_step(const char* utf8_str); + + private: + // A UTF-8 representation of 1 or more Unicode characters. + // The last element (chars[UNICHAR_LEN - 1]) is a length if + // its value < UNICHAR_LEN, otherwise it is a genuine character. + char chars[UNICHAR_LEN]; +}; + +#endif // THIRD_PARTY_TESSERACT_CCUTIL_UNICHAR_H__ diff --git a/ccutil/varable.cpp b/ccutil/varable.cpp new file mode 100644 index 0000000000..9e8f077829 --- /dev/null +++ b/ccutil/varable.cpp @@ -0,0 +1,695 @@ +/********************************************************************** + * File: varable.c (Formerly variable.c) + * Description: Initialization and setting of VARIABLEs. + * Author: Ray Smith + * Created: Fri Feb 22 16:22:34 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include +#include +#include "tprintf.h" +//#include "ipeerr.h" +#include "varable.h" +#include "scanutils.h" + +#define PLUS '+' //flag states +#define MINUS '-' +#define EQUAL '=' + +CLISTIZE (INT_VARIABLE) +CLISTIZE (BOOL_VARIABLE) CLISTIZE (STRING_VARIABLE) CLISTIZE (double_VARIABLE) +INT_VAR_FROM +INT_VARIABLE::copy; +INT_VARIABLE_CLIST +INT_VARIABLE::head; //global definition +INT_VAR_TO +INT_VARIABLE::replace; +BOOL_VAR_FROM +BOOL_VARIABLE::copy; +BOOL_VARIABLE_CLIST +BOOL_VARIABLE::head; //global definition +BOOL_VAR_TO +BOOL_VARIABLE::replace; +STRING_VAR_FROM +STRING_VARIABLE::copy; +STRING_VARIABLE_CLIST +STRING_VARIABLE::head; //global definition +STRING_VAR_TO +STRING_VARIABLE::replace; +double_VAR_FROM +double_VARIABLE::copy; +double_VARIABLE_CLIST +double_VARIABLE::head; //global definition +double_VAR_TO +double_VARIABLE::replace; + +/********************************************************************** + * INT_VAR_FROM::INT_VAR_FROM + * + * Constructor to copy the list to a temporary location while the + * list head gets constructed. + **********************************************************************/ + +INT_VAR_FROM::INT_VAR_FROM() { //constructor + INT_VARIABLE_C_IT start_it = &INT_VARIABLE::head; + INT_VARIABLE_C_IT end_it = &INT_VARIABLE::head; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + //move to copy + list.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * INT_VAR_TO::INT_VAR_TO + * + * Constructor to copy the list back to its rightful place. + **********************************************************************/ + +INT_VAR_TO::INT_VAR_TO() { //constructor + INT_VARIABLE_C_IT start_it = &INT_VARIABLE::copy.list; + INT_VARIABLE_C_IT end_it = &INT_VARIABLE::copy.list; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + INT_VARIABLE::head.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * INT_VARIABLE::INT_VARIABLE + * + * Constructor for INT_VARIABLE. Add the variable to the static list. + **********************************************************************/ + +INT_VARIABLE::INT_VARIABLE( //constructor + INT32 v, //the variable + const char *vname, //of variable + const char *comment //info on variable + ) { + INT_VARIABLE_C_IT it = &head; //list iterator + + //tprintf("Constructing %s\n",vname); + set_value(v); //set the value + name = vname; //strings must be static + info = comment; + it.add_before_stay_put (this); //add it to stack +} + + +INT_VARIABLE::~INT_VARIABLE ( //constructor +) { + INT_VARIABLE_C_IT it = &head; //list iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + if (it.data () == this) + it.extract (); +} + + +/********************************************************************** + * INT_VARIABLE::get_head + * + * Get the head of the list of the variables. + **********************************************************************/ + +INT_VARIABLE_CLIST *INT_VARIABLE::get_head() { //access to static + return &head; +} + + +/********************************************************************** + * INT_VARIABLE::print + * + * Print the entire list of INT_VARIABLEs. + **********************************************************************/ + +void INT_VARIABLE::print( //print full list + FILE *fp //file to print on + ) { + INT_VARIABLE_C_IT it = &head; //list iterator + INT_VARIABLE *elt; //current element + + if (fp == stdout) { + tprintf ("#Variables of type INT_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + tprintf ("%s %d #%s\n", elt->name, elt->value, elt->info); + } + } + else { + fprintf (fp, "#Variables of type INT_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + fprintf (fp, "%s " INT32FORMAT " #%s\n", elt->name, elt->value, + elt->info); + } + } +} + + +/********************************************************************** + * BOOL_VAR_FROM::BOOL_VAR_FROM + * + * Constructor to copy the list to a temporary location while the + * list head gets constructed. + **********************************************************************/ + +BOOL_VAR_FROM::BOOL_VAR_FROM() { //constructor + BOOL_VARIABLE_C_IT start_it = &BOOL_VARIABLE::head; + BOOL_VARIABLE_C_IT end_it = &BOOL_VARIABLE::head; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + //move to copy + list.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * BOOL_VAR_TO::BOOL_VAR_TO + * + * Constructor to copy the list back to its rightful place. + **********************************************************************/ + +BOOL_VAR_TO::BOOL_VAR_TO() { //constructor + BOOL_VARIABLE_C_IT start_it = &BOOL_VARIABLE::copy.list; + BOOL_VARIABLE_C_IT end_it = &BOOL_VARIABLE::copy.list; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + BOOL_VARIABLE::head.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * BOOL_VARIABLE::BOOL_VARIABLE + * + * Constructor for BOOL_VARIABLE. Add the variable to the static list. + **********************************************************************/ + +BOOL_VARIABLE::BOOL_VARIABLE( //constructor + BOOL8 v, //the variable + const char *vname, //of variable + const char *comment //info on variable + ) { + BOOL_VARIABLE_C_IT it = &head; //list iterator + + //tprintf("Constructing %s\n",vname); + set_value(v); //set the value + name = vname; //strings must be static + info = comment; + it.add_before_stay_put (this); //add it to stack + +} + + +/********************************************************************** + * BOOL_VARIABLE::BOOL_VARIABLE + * + * Constructor for BOOL_VARIABLE. Add the variable to the static list. + **********************************************************************/ + +BOOL_VARIABLE::~BOOL_VARIABLE () { + BOOL_VARIABLE_C_IT it = &head; //list iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + if (it.data () == this) + it.extract (); +} + + +/********************************************************************** + * BOOL_VARIABLE::get_head + * + * Get the head of the list of the variables. + **********************************************************************/ + +BOOL_VARIABLE_CLIST *BOOL_VARIABLE::get_head() { //access to static + return &head; +} + + +/********************************************************************** + * BOOL_VARIABLE::print + * + * Print the entire list of BOOL_VARIABLEs. + **********************************************************************/ + +void BOOL_VARIABLE::print( //print full list + FILE *fp //file to print on + ) { + BOOL_VARIABLE_C_IT it = &head; //list iterator + BOOL_VARIABLE *elt; //current element + + if (fp == stdout) { + tprintf ("#Variables of type BOOL_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + tprintf ("%s %c #%s\n", + elt->name, elt->value ? 'T' : 'F', elt->info); + } + } + else { + fprintf (fp, "#Variables of type BOOL_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + fprintf (fp, "%s %c #%s\n", + elt->name, elt->value ? 'T' : 'F', elt->info); + } + } +} + + +/********************************************************************** + * STRING_VAR_FROM::STRING_VAR_FROM + * + * Constructor to copy the list to a temporary location while the + * list head gets constructed. + **********************************************************************/ + +STRING_VAR_FROM::STRING_VAR_FROM() { //constructor + STRING_VARIABLE_C_IT start_it = &STRING_VARIABLE::head; + STRING_VARIABLE_C_IT end_it = &STRING_VARIABLE::head; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + //move to copy + list.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * STRING_VAR_TO::STRING_VAR_TO + * + * Constructor to copy the list back to its rightful place. + **********************************************************************/ + +STRING_VAR_TO::STRING_VAR_TO() { //constructor + STRING_VARIABLE_C_IT start_it = &STRING_VARIABLE::copy.list; + STRING_VARIABLE_C_IT end_it = &STRING_VARIABLE::copy.list; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + STRING_VARIABLE::head.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * STRING_VARIABLE::STRING_VARIABLE + * + * Constructor for STRING_VARIABLE. Add the variable to the static list. + **********************************************************************/ + +STRING_VARIABLE::STRING_VARIABLE ( + //constructor +const char *v, //the variable +const char *vname, //of variable +const char *comment //info on variable +): +value(v) { + //list iterator + STRING_VARIABLE_C_IT it = &head; + + //tprintf("Constructing %s\n",vname); + name = vname; //strings must be static + info = comment; + it.add_before_stay_put (this); //add it to stack +} + + +/********************************************************************** + * STRING_VARIABLE::~STRING_VARIABLE + * + * Destructor for STRING_VARIABLE. Add the variable to the static list. + **********************************************************************/ + + //constructor +STRING_VARIABLE::~STRING_VARIABLE ( +) { + //list iterator + STRING_VARIABLE_C_IT it = &head; + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + if (it.data () == this) + it.extract (); +} + + +/********************************************************************** + * STRING_VARIABLE::get_head + * + * Get the head of the list of the variables. + **********************************************************************/ + +STRING_VARIABLE_CLIST *STRING_VARIABLE::get_head() { //access to static + return &head; +} + + +/********************************************************************** + * STRING_VARIABLE::print + * + * Print the entire list of STRING_VARIABLEs. + **********************************************************************/ + +void STRING_VARIABLE::print( //print full list + FILE *fp //file to print on + ) { + //list iterator + STRING_VARIABLE_C_IT it = &head; + STRING_VARIABLE *elt; //current element + + if (fp == stdout) { + tprintf ("#Variables of type STRING_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + tprintf ("%s #%s %s\n", elt->name, elt->value.string (), elt->info); + } + } + else { + fprintf (fp, "#Variables of type STRING_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + fprintf (fp, "%s #%s %s\n", + elt->name, elt->value.string (), elt->info); + } + } +} + + +/********************************************************************** + * double_VAR_FROM::double_VAR_FROM + * + * Constructor to copy the list to a temporary location while the + * list head gets constructed. + **********************************************************************/ + +double_VAR_FROM::double_VAR_FROM() { //constructor + double_VARIABLE_C_IT start_it = &double_VARIABLE::head; + double_VARIABLE_C_IT end_it = &double_VARIABLE::head; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + //move to copy + list.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * double_VAR_TO::double_VAR_TO + * + * Constructor to copy the list back to its rightful place. + **********************************************************************/ + +double_VAR_TO::double_VAR_TO() { //constructor + double_VARIABLE_C_IT start_it = &double_VARIABLE::copy.list; + double_VARIABLE_C_IT end_it = &double_VARIABLE::copy.list; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + double_VARIABLE::head.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * double_VARIABLE::double_VARIABLE + * + * Constructor for double_VARIABLE. Add the variable to the static list. + **********************************************************************/ + +double_VARIABLE::double_VARIABLE( //constructor + double v, //the variable + const char *vname, //of variable + const char *comment //info on variable + ) { + //list iterator + double_VARIABLE_C_IT it = &head; + + //tprintf("Constructing %s\n",vname); + set_value(v); //set the value + name = vname; //strings must be static + info = comment; + it.add_before_stay_put (this); //add it to stack +} + + +double_VARIABLE::~double_VARIABLE () { + //list iterator + double_VARIABLE_C_IT it = &head; + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + if (it.data () == this) + it.extract (); +} + + +/********************************************************************** + * double_VARIABLE::get_head + * + * Get the head of the list of the variables. + **********************************************************************/ + +double_VARIABLE_CLIST *double_VARIABLE::get_head() { //access to static + return &head; +} + + +/********************************************************************** + * double_VARIABLE::print + * + * Print the entire list of double_VARIABLEs. + **********************************************************************/ + +void double_VARIABLE::print( //print full list + FILE *fp //file to print on + ) { + //list iterator + double_VARIABLE_C_IT it = &head; + double_VARIABLE *elt; //current element + + if (fp == stdout) { + tprintf ("#Variables of type double_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + tprintf ("%s %lg #%s\n", elt->name, elt->value, elt->info); + } + } + else { + fprintf (fp, "#Variables of type double_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + fprintf (fp, "%s %g #%s\n", elt->name, elt->value, elt->info); + } + } +} + + +/********************************************************************** + * read_variables_file + * + * Read a file of variables definitions and set/modify the values therein. + * If the filename begins with a + or -, the BOOL_VARIABLEs will be + * ORed or ANDed with any current values. + * Blank lines and lines beginning # are ignored. + * Values may have any whitespace after the name and are the rest of line. + **********************************************************************/ + +DLLSYM BOOL8 read_variables_file( //read the file + const char *file //name to read + ) { + BOOL8 anyerr; //true if any error + char flag; //file flag + BOOL8 foundit; //found variable + INT16 length; //length of line + INT16 nameoffset; //offset for real name + char *valptr; //value field + char *stringend; //end of string value + FILE *fp; //file pointer + INT32 intval; //value from file + double doubleval; //value form file + //iterators + INT_VARIABLE_C_IT int_it = &INT_VARIABLE::head; + BOOL_VARIABLE_C_IT BOOL_it = &BOOL_VARIABLE::head; + STRING_VARIABLE_C_IT STRING_it = &STRING_VARIABLE::head; + double_VARIABLE_C_IT double_it = &double_VARIABLE::head; + char line[MAX_PATH]; //input line + + anyerr = FALSE; + if (*file == PLUS) { + flag = PLUS; //file has flag + nameoffset = 1; + } + else if (*file == MINUS) { + flag = MINUS; + nameoffset = 1; + } + else { + flag = EQUAL; + nameoffset = 0; + } + + fp = fopen (file + nameoffset, "r"); + if (fp == NULL) { + tprintf ("read_variables_file:Can't open %s", file + nameoffset); + return TRUE; //can't open it + } + while (fgets (line, MAX_PATH, fp)) { + if (line[0] != '\n' && line[0] != '#') { + length = strlen (line); + if (line[length - 1] == '\n') + line[length - 1] = '\0'; //cut newline + for (valptr = line; *valptr && *valptr != ' ' && *valptr != '\t'; + valptr++); + if (*valptr) { //found blank + *valptr = '\0'; //make name a string + do + + valptr++; //find end of blanks + while (*valptr == ' ' || *valptr == '\t'); + + if (*valptr && *valptr != '#') { + //last char in string + stringend = valptr + strlen (valptr) - 1; + while (stringend != valptr) { + while (stringend != valptr + && (*stringend == ' ' || *stringend == '\t')) + //cut trailing blanks + stringend--; + stringend[1] = '\0'; //terminate string + + while (stringend != valptr + && (*stringend != ' ' && *stringend != '\t' + || stringend[1] != '#')) + stringend--; //find word start + } + } + } + foundit = FALSE; + + //find name + for (STRING_it.mark_cycle_pt (); !STRING_it.cycled_list () && strcmp (line, STRING_it.data ()->name); STRING_it.forward ()); + //found it + if (!STRING_it.cycled_list ()) { + foundit = TRUE; //found the varaible + if (*valptr == '\0' || *valptr == '#') + STRING_it.data ()->set_value ((char *) NULL); + //no value + else + //set its value + STRING_it.data ()->set_value (valptr); + } + + if (*valptr) { + //find name + for (int_it.mark_cycle_pt (); !int_it.cycled_list () && strcmp (line, int_it.data ()->name); int_it.forward ()); + //found it + if (!int_it.cycled_list () + && sscanf (valptr, INT32FORMAT, &intval) == 1) { + foundit = TRUE; //found the varaible + //set its value + int_it.data ()->set_value (intval); + } + + //find name + for (BOOL_it.mark_cycle_pt (); !BOOL_it.cycled_list () && strcmp (line, BOOL_it.data ()->name); BOOL_it.forward ()); + //found it + if (!BOOL_it.cycled_list ()) { + if (*valptr == 'T' || *valptr == 't' + || *valptr == 'Y' || *valptr == 'y' || *valptr == '1') { + foundit = TRUE; + if (flag == MINUS) + BOOL_it.data ()->set_value (FALSE); + //set to false + else + BOOL_it.data ()->set_value (TRUE); + //set to true + } + else if (*valptr == 'F' || *valptr == 'f' + || *valptr == 'N' || *valptr == 'n' + || *valptr == '0') { + foundit = TRUE; + if (flag == EQUAL) + BOOL_it.data ()->set_value (FALSE); + //set to false + } + } + + //find name + for (double_it.mark_cycle_pt (); !double_it.cycled_list () && strcmp (line, double_it.data ()->name); double_it.forward ()); + //found it + + #ifdef EMBEDDED + if (!double_it.cycled_list ()) { + doubleval = strtofloat(valptr); + #else + if (!double_it.cycled_list () + && sscanf (valptr, "%lf", &doubleval) == 1) { + #endif + foundit = TRUE; //found the varaible + double_it.data ()->set_value (doubleval); + //set its value + } + + if (!foundit) { + anyerr = TRUE; //had an error + tprintf ("read_variables_file:variable not found: %s", + line); + } + } + else if (!foundit) { + anyerr = TRUE; //had an error + tprintf ("read_variables_file:No value for variable %s", line); + } + } + } + fclose(fp); //close file + return anyerr; +} + + +/********************************************************************** + * print_variables + * + * Print all variable types to the given file + **********************************************************************/ + +DLLSYM void print_variables( //print all vars + FILE *fp //file to print on + ) { + INT_VARIABLE::print(fp); //print INTs + BOOL_VARIABLE::print(fp); //print BOOLs + STRING_VARIABLE::print(fp); //print STRINGs + double_VARIABLE::print(fp); //print doubles +} diff --git a/ccutil/varable.h b/ccutil/varable.h new file mode 100644 index 0000000000..8f92bf8e8c --- /dev/null +++ b/ccutil/varable.h @@ -0,0 +1,412 @@ +/********************************************************************** + * File: varable.h (Formerly variable.h) + * Description: Class definitions of the *_VAR classes for tunable constants. + * Author: Ray Smith + * Created: Fri Feb 22 11:26:25 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef VARABLE_H +#define VARABLE_H + +#include +#include "clst.h" +#include "strngs.h" + +class DLLSYM INT_VARIABLE; + + //read the file +extern DLLSYM BOOL8 read_variables_file(const char *file //name to read + ); + //print all vars +extern DLLSYM void print_variables(FILE *fp //file to print on + ); + +CLISTIZEH (INT_VARIABLE) +class DLLSYM INT_VAR_FROM +{ + friend class INT_VAR_TO; + public: + INT_VAR_FROM(); //constructor + private: + INT_VARIABLE_CLIST list; //copy of list +}; + +class DLLSYM INT_VAR_TO +{ + public: + INT_VAR_TO(); //constructor + private: + INT_VARIABLE_CLIST dummy; +}; + +class DLLSYM INT_VARIABLE +{ + friend class INT_VAR_TO; + friend class INT_VAR_FROM; + //for setting values + friend DLLSYM BOOL8 read_variables_file(const char *file); //file to read + + public: + INT_VARIABLE( //constructor + INT32 v, //initial value + const char *vname, //name of variable + const char *comment); //info on variable + + INT_VARIABLE() { //for elist only + value = 0; + name = "NONAME"; + info = "Uninitialized"; + } + ~INT_VARIABLE (); //for elist only + + operator INT32() { //conversion + return value; //access as int + } + + void set_value( //assign to value + INT32 v) { //value to set + value = v; + } + + const char *name_str() { //access name + return name; + } + + const char *info_str() { //access name + return info; + } + + //access list head + static INT_VARIABLE_CLIST *get_head(); + + static void print( //print whole list + FILE *fp); //file to print on + + private: + INT32 value; //the variable + const char *name; //name of variable + const char *info; //for menus + static INT_VAR_FROM copy; //pre constructor + //start of list + static INT_VARIABLE_CLIST head; + static INT_VAR_TO replace; //post constructor +}; + +class DLLSYM BOOL_VARIABLE; + +CLISTIZEH (BOOL_VARIABLE) +class DLLSYM BOOL_VAR_FROM +{ + friend class BOOL_VAR_TO; + public: + BOOL_VAR_FROM(); //constructor + private: + BOOL_VARIABLE_CLIST list; //copy of list +}; + +class DLLSYM BOOL_VAR_TO +{ + public: + BOOL_VAR_TO(); //constructor + private: + BOOL_VARIABLE_CLIST dummy; +}; + +class DLLSYM BOOL_VARIABLE +{ + friend class BOOL_VAR_FROM; + friend class BOOL_VAR_TO; + //for setting values + friend DLLSYM BOOL8 read_variables_file(const char *file); //file to read + + public: + BOOL_VARIABLE( //constructor + BOOL8 v, //initial value + const char *vname, //name of variable + const char *comment); //info on variable + + BOOL_VARIABLE() { //for elist only + value = FALSE; + name = "NONAME"; + info = "Uninitialized"; + } + ~BOOL_VARIABLE (); //for elist only + + operator BOOL8() { //conversion + return value; //access as int + } + + void set_value( //assign to value + BOOL8 v) { //value to set + value = v; + } + + const char *name_str() { //access name + return name; + } + + const char *info_str() { //access name + return info; + } + + //access list head + static BOOL_VARIABLE_CLIST *get_head(); + + static void print( //print whole list + FILE *fp); //file to print on + + private: + BOOL8 value; //the variable + const char *name; //name of variable + const char *info; //for menus + static BOOL_VAR_FROM copy; //pre constructor + //start of list + static BOOL_VARIABLE_CLIST head; + static BOOL_VAR_TO replace; //post constructor +}; + +class DLLSYM STRING_VARIABLE; + +CLISTIZEH (STRING_VARIABLE) +class DLLSYM STRING_VAR_FROM +{ + friend class STRING_VAR_TO; + public: + STRING_VAR_FROM(); //constructor + private: + STRING_VARIABLE_CLIST list; //copy of list +}; + +class DLLSYM STRING_VAR_TO +{ + public: + STRING_VAR_TO(); //constructor + private: + STRING_VARIABLE_CLIST dummy; +}; + +class DLLSYM STRING_VARIABLE +{ + friend class STRING_VAR_TO; + friend class STRING_VAR_FROM; + //for setting values + friend DLLSYM BOOL8 read_variables_file(const char *file); //file to read + + public: + STRING_VARIABLE( //constructor + const char *v, //initial value + const char *vname, //name of variable + const char *comment); //info on variable + + STRING_VARIABLE() { //for elist only + name = "NONAME"; + info = "Uninitialized"; + } + ~STRING_VARIABLE (); //for elist only + + //conversion + operator const STRING &() { + return value; //access as int + } + + void set_value( //assign to value + STRING v) { //value to set + value = v; + } + + const char *string() const { //get string + return value.string (); + } + + const char *name_str() { //access name + return name; + } + + const char *info_str() { //access name + return info; + } + + //access list head + static STRING_VARIABLE_CLIST *get_head(); + + static void print( //print whole list + FILE *fp); //file to print on + + private: + STRING value; //the variable + const char *name; //name of variable + const char *info; //for menus + static STRING_VAR_FROM copy; //pre constructor + //start of list + static STRING_VARIABLE_CLIST head; + static STRING_VAR_TO replace;//post constructor +}; + +class DLLSYM double_VARIABLE; + +CLISTIZEH (double_VARIABLE) +class DLLSYM double_VAR_FROM +{ + friend class double_VAR_TO; + public: + double_VAR_FROM(); //constructor + private: + double_VARIABLE_CLIST list; //copy of list +}; + +class DLLSYM double_VAR_TO +{ + public: + double_VAR_TO(); //constructor + private: + double_VARIABLE_CLIST dummy; +}; + +class DLLSYM double_VARIABLE +{ + friend class double_VAR_TO; + friend class double_VAR_FROM; + //for setting values + friend DLLSYM BOOL8 read_variables_file(const char *file); //file to read + + public: + double_VARIABLE( //constructor + double v, //initial value + const char *vname, //name of variable + const char *comment); //info on variable + + double_VARIABLE() { //for elist only + value = 0.0; + name = "NONAME"; + info = "Uninitialized"; + } + ~double_VARIABLE (); //for elist only + + operator double() { //conversion + return value; //access as int + } + + void set_value( //assign to value + double v) { //value to set + value = v; + } + + const char *name_str() { //access name + return name; + } + + const char *info_str() { //access name + return info; + } + + //access list head + static double_VARIABLE_CLIST *get_head(); + + static void print( //print whole list + FILE *fp); //file to print on + + private: + double value; //the variable + const char *name; //name of variable + const char *info; //for menus + static double_VAR_FROM copy; //pre constructor + //start of list + static double_VARIABLE_CLIST head; + static double_VAR_TO replace;//post constructor +}; + +/************************************************************************* + * NOTE ON DEFINING VARIABLES + * + * For our normal code, the ***_VAR and ***_EVAR macros for variable + * definitions are identical. HOWEVER, for the code version to ship to NEVADA + * (or anywhere else where we want to hide the majority of variables) the + * **_VAR macros are changed so that the "#name" and "comment" parameters + * to the variable constructor are changed to empty strings. This prevents the + * variable name or comment string appearing in the object code file (after it + * has gone through strip). + * + * Certain variables can remain EXPOSED and hence be used in config files given + * to UNLV. These are variable which have been declared with the ***_EVAR + * macros. + * + *************************************************************************/ + +/* SECURE_NAMES is defined in senames.h when necessary */ +#ifdef SECURE_NAMES + +#define INT_VAR(name,val,comment) /*make INT_VARIABLE*/\ + INT_VARIABLE name(val,"","") + +#define BOOL_VAR(name,val,comment) /*make BOOL_VARIABLE*/\ + BOOL_VARIABLE name(val,"","") + +#define STRING_VAR(name,val,comment) /*make STRING_VARIABLE*/\ + STRING_VARIABLE name(val,"","") + +#define double_VAR(name,val,comment) /*make double_VARIABLE*/\ + double_VARIABLE name(val,"","") + +#else + +#define INT_VAR(name,val,comment) /*make INT_VARIABLE*/\ + INT_VARIABLE name(val,#name,comment) + +#define BOOL_VAR(name,val,comment) /*make BOOL_VARIABLE*/\ + BOOL_VARIABLE name(val,#name,comment) + +#define STRING_VAR(name,val,comment) /*make STRING_VARIABLE*/\ + STRING_VARIABLE name(val,#name,comment) + +#define double_VAR(name,val,comment) /*make double_VARIABLE*/\ + double_VARIABLE name(val,#name,comment) +#endif + +#define INT_VAR_H(name,val,comment) /*declare one*/\ + INT_VARIABLE name + +#define BOOL_VAR_H(name,val,comment) /*declare one*/\ + BOOL_VARIABLE name + +#define STRING_VAR_H(name,val,comment) /*declare one*/\ + STRING_VARIABLE name + +#define double_VAR_H(name,val,comment) /*declare one*/\ + double_VARIABLE name + +#define INT_EVAR(name,val,comment) /*make INT_VARIABLE*/\ + INT_VARIABLE name(val,#name,comment) + +#define INT_EVAR_H(name,val,comment) /*declare one*/\ + INT_VARIABLE name + +#define BOOL_EVAR(name,val,comment) /*make BOOL_VARIABLE*/\ + BOOL_VARIABLE name(val,#name,comment) + +#define BOOL_EVAR_H(name,val,comment) /*declare one*/\ + BOOL_VARIABLE name + +#define STRING_EVAR(name,val,comment) /*make STRING_VARIABLE*/\ + STRING_VARIABLE name(val,#name,comment) + +#define STRING_EVAR_H(name,val,comment) /*declare one*/\ + STRING_VARIABLE name + +#define double_EVAR(name,val,comment) /*make double_VARIABLE*/\ + double_VARIABLE name(val,#name,comment) + +#define double_EVAR_H(name,val,comment) /*declare one*/\ + double_VARIABLE name +#endif diff --git a/classify/Makefile.am b/classify/Makefile.am new file mode 100644 index 0000000000..27c782a1a6 --- /dev/null +++ b/classify/Makefile.am @@ -0,0 +1,24 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil \ + -I$(top_srcdir)/ccstruct -I$(top_srcdir)/dict \ + -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + adaptive.h adaptmatch.h baseline.h blobclass.h chartoname.h \ + cluster.h clusttool.h cutoffs.h extern.h extract.h featdefs.h \ + flexfx.h float2int.h fpoint.h fxdefs.h fxid.h hideedge.h \ + intfx.h intmatcher.h intproto.h kdtree.h mfdefs.h mf.h \ + mfoutline.h mfx.h normfeat.h normmatch.h ocrfeatures.h \ + outfeat.h picofeat.h protos.h sigmenu.h speckle.h xform2d.h + +noinst_LIBRARIES = libtesseract_classify.a +libtesseract_classify_a_SOURCES = \ + adaptive.cpp adaptmatch.cpp baseline.cpp blobclass.cpp \ + chartoname.cpp cluster.cpp clusttool.cpp cutoffs.cpp \ + extract.cpp featdefs.cpp flexfx.cpp float2int.cpp \ + fpoint.cpp fxdefs.cpp hideedge.cpp intfx.cpp intmatcher.cpp \ + intproto.cpp kdtree.cpp mf.cpp mfdefs.cpp mfoutline.cpp \ + mfx.cpp normfeat.cpp normmatch.cpp ocrfeatures.cpp \ + outfeat.cpp picofeat.cpp protos.cpp sigmenu.cpp speckle.cpp \ + xform2d.cpp diff --git a/classify/Makefile.in b/classify/Makefile.in new file mode 100644 index 0000000000..ba6c6243e3 --- /dev/null +++ b/classify/Makefile.in @@ -0,0 +1,584 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = classify +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_classify_a_AR = $(AR) $(ARFLAGS) +libtesseract_classify_a_LIBADD = +am_libtesseract_classify_a_OBJECTS = adaptive.$(OBJEXT) \ + adaptmatch.$(OBJEXT) baseline.$(OBJEXT) blobclass.$(OBJEXT) \ + chartoname.$(OBJEXT) cluster.$(OBJEXT) clusttool.$(OBJEXT) \ + cutoffs.$(OBJEXT) extract.$(OBJEXT) featdefs.$(OBJEXT) \ + flexfx.$(OBJEXT) float2int.$(OBJEXT) fpoint.$(OBJEXT) \ + fxdefs.$(OBJEXT) hideedge.$(OBJEXT) intfx.$(OBJEXT) \ + intmatcher.$(OBJEXT) intproto.$(OBJEXT) kdtree.$(OBJEXT) \ + mf.$(OBJEXT) mfdefs.$(OBJEXT) mfoutline.$(OBJEXT) \ + mfx.$(OBJEXT) normfeat.$(OBJEXT) normmatch.$(OBJEXT) \ + ocrfeatures.$(OBJEXT) outfeat.$(OBJEXT) picofeat.$(OBJEXT) \ + protos.$(OBJEXT) sigmenu.$(OBJEXT) speckle.$(OBJEXT) \ + xform2d.$(OBJEXT) +libtesseract_classify_a_OBJECTS = \ + $(am_libtesseract_classify_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_classify_a_SOURCES) +DIST_SOURCES = $(libtesseract_classify_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil \ + -I$(top_srcdir)/ccstruct -I$(top_srcdir)/dict \ + -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + adaptive.h adaptmatch.h baseline.h blobclass.h chartoname.h \ + cluster.h clusttool.h cutoffs.h extern.h extract.h featdefs.h \ + flexfx.h float2int.h fpoint.h fxdefs.h fxid.h hideedge.h \ + intfx.h intmatcher.h intproto.h kdtree.h mfdefs.h mf.h \ + mfoutline.h mfx.h normfeat.h normmatch.h ocrfeatures.h \ + outfeat.h picofeat.h protos.h sigmenu.h speckle.h xform2d.h + +noinst_LIBRARIES = libtesseract_classify.a +libtesseract_classify_a_SOURCES = \ + adaptive.cpp adaptmatch.cpp baseline.cpp blobclass.cpp \ + chartoname.cpp cluster.cpp clusttool.cpp cutoffs.cpp \ + extract.cpp featdefs.cpp flexfx.cpp float2int.cpp \ + fpoint.cpp fxdefs.cpp hideedge.cpp intfx.cpp intmatcher.cpp \ + intproto.cpp kdtree.cpp mf.cpp mfdefs.cpp mfoutline.cpp \ + mfx.cpp normfeat.cpp normmatch.cpp ocrfeatures.cpp \ + outfeat.cpp picofeat.cpp protos.cpp sigmenu.cpp speckle.cpp \ + xform2d.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu classify/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu classify/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_classify.a: $(libtesseract_classify_a_OBJECTS) $(libtesseract_classify_a_DEPENDENCIES) + -rm -f libtesseract_classify.a + $(libtesseract_classify_a_AR) libtesseract_classify.a $(libtesseract_classify_a_OBJECTS) $(libtesseract_classify_a_LIBADD) + $(RANLIB) libtesseract_classify.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adaptive.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adaptmatch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/baseline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobclass.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chartoname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cluster.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clusttool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cutoffs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extract.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/featdefs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flexfx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/float2int.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fpoint.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fxdefs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hideedge.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intfx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intmatcher.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intproto.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kdtree.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfdefs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfoutline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normfeat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normmatch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrfeatures.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/outfeat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/picofeat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protos.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sigmenu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/speckle.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xform2d.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/classify/adaptive.cpp b/classify/adaptive.cpp new file mode 100644 index 0000000000..324fbd1445 --- /dev/null +++ b/classify/adaptive.cpp @@ -0,0 +1,534 @@ +/****************************************************************************** + ** Filename: adaptive.c + ** Purpose: Adaptive matcher. + ** Author: Dan Johnson + ** History: Fri Mar 8 10:00:21 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "adaptive.h" +#include "emalloc.h" +#include "freelist.h" + +#ifdef __UNIX__ +#include +#endif +#include + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int AddAdaptedClass(ADAPT_TEMPLATES Templates, + ADAPT_CLASS Class, + CLASS_ID ClassId) { +/* + ** Parameters: + ** Templates set of templates to add new class to + ** Class new class to add to templates + ** ClassId class id to associate with new class + ** Globals: none + ** Operation: This routine adds a new adapted class to an existing + ** set of adapted templates. + ** Return: The class index of the new class. + ** Exceptions: none + ** History: Thu Mar 14 13:06:09 1991, DSJ, Created. + */ + INT_CLASS IntClass; + CLASS_INDEX ClassIndex; + + assert (Templates != NULL); + assert (Class != NULL); + assert (LegalClassId (ClassId)); + assert (UnusedClassIdIn (Templates->Templates, ClassId)); + assert (Class->NumPermConfigs == 0); + + IntClass = NewIntClass (1, 1); + ClassIndex = AddIntClass (Templates->Templates, ClassId, IntClass); + + assert (Templates->Class[ClassIndex] == NULL); + + Templates->Class[ClassIndex] = Class; + + return (ClassIndex); + +} /* AddAdaptedClass */ + + +/*---------------------------------------------------------------------------*/ +void FreeTempConfig(TEMP_CONFIG Config) { +/* + ** Parameters: + ** Config config to be freed + ** Globals: none + ** Operation: This routine frees all memory consumed by a temporary + ** configuration. + ** Return: none + ** Exceptions: none + ** History: Thu Mar 14 13:34:23 1991, DSJ, Created. + */ + assert (Config != NULL); + + destroy_nodes (Config->ContextsSeen, memfree); + FreeBitVector (Config->Protos); + c_free_struct (Config, sizeof (TEMP_CONFIG_STRUCT), "TEMP_CONFIG_STRUCT"); + +} /* FreeTempConfig */ + + +/*---------------------------------------------------------------------------*/ +void FreeTempProto(void *arg) { + PROTO proto = (PROTO) arg; + + c_free_struct (proto, sizeof (TEMP_PROTO_STRUCT), "TEMP_PROTO_STRUCT"); +} + + +/*---------------------------------------------------------------------------*/ +ADAPT_CLASS NewAdaptedClass() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This operation allocates and initializes a new adapted + ** class data structure and returns a ptr to it. + ** Return: Ptr to new class data structure. + ** Exceptions: none + ** History: Thu Mar 14 12:58:13 1991, DSJ, Created. + */ + ADAPT_CLASS Class; + int i; + + Class = (ADAPT_CLASS) Emalloc (sizeof (ADAPT_CLASS_STRUCT)); + Class->NumPermConfigs = 0; + Class->TempProtos = NIL; + + Class->PermProtos = NewBitVector (MAX_NUM_PROTOS); + Class->PermConfigs = NewBitVector (MAX_NUM_CONFIGS); + zero_all_bits (Class->PermProtos, WordsInVectorOfSize (MAX_NUM_PROTOS)); + zero_all_bits (Class->PermConfigs, WordsInVectorOfSize (MAX_NUM_CONFIGS)); + + for (i = 0; i < MAX_NUM_CONFIGS; i++) + TempConfigFor (Class, i) = NULL; + + return (Class); + +} /* NewAdaptedClass */ + + +/*-------------------------------------------------------------------------*/ +void free_adapted_class(ADAPT_CLASS adapt_class) { + int i; + + for (i = 0; i < MAX_NUM_CONFIGS; i++) { + if (ConfigIsPermanent (adapt_class, i) + && PermConfigFor (adapt_class, i) != NULL) + Efree (PermConfigFor (adapt_class, i)); + else if (!ConfigIsPermanent (adapt_class, i) + && TempConfigFor (adapt_class, i) != NULL) + FreeTempConfig (TempConfigFor (adapt_class, i)); + } + FreeBitVector (adapt_class->PermProtos); + FreeBitVector (adapt_class->PermConfigs); + destroy_nodes (adapt_class->TempProtos, FreeTempProto); + Efree(adapt_class); +} + + +/*---------------------------------------------------------------------------*/ +ADAPT_TEMPLATES NewAdaptedTemplates() { +/* + ** Parameters: none + ** Globals: none + ** Operation: + ** Return: none + ** Exceptions: none + ** History: Fri Mar 8 10:15:28 1991, DSJ, Created. + */ + ADAPT_TEMPLATES Templates; + int i; + + Templates = (ADAPT_TEMPLATES) Emalloc (sizeof (ADAPT_TEMPLATES_STRUCT)); + + Templates->Templates = NewIntTemplates (); + Templates->NumPermClasses = 0; + + for (i = 0; i < MAX_NUM_CLASSES; i++) + Templates->Class[i] = NULL; + + return (Templates); + +} /* NewAdaptedTemplates */ + + +/*-------------------------------------------------------------------------------*/ +void free_adapted_templates(ADAPT_TEMPLATES templates) { + + if (templates != NULL) { + int i; + for (i = 0; i < NumClassesIn (templates->Templates); i++) + free_adapted_class (templates->Class[i]); + free_int_templates (templates->Templates); + Efree(templates); + } +} + + +/*---------------------------------------------------------------------------*/ +TEMP_CONFIG NewTempConfig(int MaxProtoId) { +/* + ** Parameters: + ** MaxProtoId max id of any proto in new config + ** Globals: none + ** Operation: This routine allocates and returns a new temporary + ** config. + ** Return: Ptr to new temp config. + ** Exceptions: none + ** History: Thu Mar 14 13:28:21 1991, DSJ, Created. + */ + TEMP_CONFIG Config; + int NumProtos = MaxProtoId + 1; + + Config = + (TEMP_CONFIG) c_alloc_struct (sizeof (TEMP_CONFIG_STRUCT), + "TEMP_CONFIG_STRUCT"); + Config->Protos = NewBitVector (NumProtos); + + Config->NumTimesSeen = 1; + Config->MaxProtoId = MaxProtoId; + Config->ProtoVectorSize = WordsInVectorOfSize (NumProtos); + Config->ContextsSeen = NIL; + zero_all_bits (Config->Protos, Config->ProtoVectorSize); + + return (Config); + +} /* NewTempConfig */ + + +/*---------------------------------------------------------------------------*/ +TEMP_PROTO NewTempProto() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine allocates and returns a new temporary proto. + ** Return: Ptr to new temporary proto. + ** Exceptions: none + ** History: Thu Mar 14 13:31:31 1991, DSJ, Created. + */ + return ((TEMP_PROTO) + c_alloc_struct (sizeof (TEMP_PROTO_STRUCT), "TEMP_PROTO_STRUCT")); +} /* NewTempProto */ + + +/*---------------------------------------------------------------------------*/ +void PrintAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates) { +/* + ** Parameters: + ** File open text file to print Templates to + ** Templates adapted templates to print to File + ** Globals: none + ** Operation: This routine prints a summary of the adapted templates + ** in Templates to File. + ** Return: none + ** Exceptions: none + ** History: Wed Mar 20 13:35:29 1991, DSJ, Created. + */ + int i; + INT_CLASS IClass; + ADAPT_CLASS AClass; + + #ifndef SECURE_NAMES + fprintf (File, "\n\nSUMMARY OF ADAPTED TEMPLATES:\n\n"); + fprintf (File, "Num classes = %d; Num permanent classes = %d\n\n", + NumClassesIn (Templates->Templates), Templates->NumPermClasses); + fprintf (File, "Index Id NC NPC NP NPP\n"); + fprintf (File, "------------------------\n"); + + for (i = 0; i < NumClassesIn (Templates->Templates); i++) { + IClass = ClassForIndex (Templates->Templates, i); + AClass = Templates->Class[i]; + + fprintf (File, "%5d %c %3d %3d %3d %3d\n", + i, ClassIdForIndex (Templates->Templates, i), + NumIntConfigsIn (IClass), AClass->NumPermConfigs, + NumIntProtosIn (IClass), + NumIntProtosIn (IClass) - count (AClass->TempProtos)); + } + #endif + fprintf (File, "\n"); + +} /* PrintAdaptedTemplates */ + + +/*---------------------------------------------------------------------------*/ +ADAPT_CLASS ReadAdaptedClass(FILE *File) { +/* + ** Parameters: + ** File open file to read adapted class from + ** Globals: none + ** Operation: Read an adapted class description from File and return + ** a ptr to the adapted class. + ** Return: Ptr to new adapted class. + ** Exceptions: none + ** History: Tue Mar 19 14:11:01 1991, DSJ, Created. + */ + int NumTempProtos; + int NumConfigs; + int i; + ADAPT_CLASS Class; + TEMP_PROTO TempProto; + + /* first read high level adapted class structure */ + Class = (ADAPT_CLASS) Emalloc (sizeof (ADAPT_CLASS_STRUCT)); + fread ((char *) Class, sizeof (ADAPT_CLASS_STRUCT), 1, File); + + /* then read in the definitions of the permanent protos and configs */ + Class->PermProtos = NewBitVector (MAX_NUM_PROTOS); + Class->PermConfigs = NewBitVector (MAX_NUM_CONFIGS); + fread ((char *) Class->PermProtos, sizeof (UINT32), + WordsInVectorOfSize (MAX_NUM_PROTOS), File); + fread ((char *) Class->PermConfigs, sizeof (UINT32), + WordsInVectorOfSize (MAX_NUM_CONFIGS), File); + + /* then read in the list of temporary protos */ + fread ((char *) &NumTempProtos, sizeof (int), 1, File); + Class->TempProtos = NIL; + for (i = 0; i < NumTempProtos; i++) { + TempProto = + (TEMP_PROTO) c_alloc_struct (sizeof (TEMP_PROTO_STRUCT), + "TEMP_PROTO_STRUCT"); + fread ((char *) TempProto, sizeof (TEMP_PROTO_STRUCT), 1, File); + Class->TempProtos = push_last (Class->TempProtos, TempProto); + } + + /* then read in the adapted configs */ + fread ((char *) &NumConfigs, sizeof (int), 1, File); + for (i = 0; i < NumConfigs; i++) + if (test_bit (Class->PermConfigs, i)) + Class->Config[i].Perm = ReadPermConfig (File); + else + Class->Config[i].Temp = ReadTempConfig (File); + + return (Class); + +} /* ReadAdaptedClass */ + + +/*---------------------------------------------------------------------------*/ +ADAPT_TEMPLATES ReadAdaptedTemplates(FILE *File) { +/* + ** Parameters: + ** File open text file to read adapted templates from + ** Globals: none + ** Operation: Read a set of adapted templates from File and return + ** a ptr to the templates. + ** Return: Ptr to adapted templates read from File. + ** Exceptions: none + ** History: Mon Mar 18 15:18:10 1991, DSJ, Created. + */ + int i; + ADAPT_TEMPLATES Templates; + + /* first read the high level adaptive template struct */ + Templates = (ADAPT_TEMPLATES) Emalloc (sizeof (ADAPT_TEMPLATES_STRUCT)); + fread ((char *) Templates, sizeof (ADAPT_TEMPLATES_STRUCT), 1, File); + + /* then read in the basic integer templates */ + Templates->Templates = ReadIntTemplates (File, FALSE); + + /* then read in the adaptive info for each class */ + for (i = 0; i < NumClassesIn (Templates->Templates); i++) { + Templates->Class[i] = ReadAdaptedClass (File); + } + return (Templates); + +} /* ReadAdaptedTemplates */ + + +/*---------------------------------------------------------------------------*/ +PERM_CONFIG ReadPermConfig(FILE *File) { +/* + ** Parameters: + ** File open file to read permanent config from + ** Globals: none + ** Operation: Read a permanent configuration description from File + ** and return a ptr to it. + ** Return: Ptr to new permanent configuration description. + ** Exceptions: none + ** History: Tue Mar 19 14:25:26 1991, DSJ, Created. + */ + PERM_CONFIG Config; + UINT8 NumAmbigs; + + fread ((char *) &NumAmbigs, sizeof (UINT8), 1, File); + Config = (PERM_CONFIG) Emalloc (sizeof (char) * (NumAmbigs + 1)); + fread (Config, sizeof (char), NumAmbigs, File); + Config[NumAmbigs] = '\0'; + + return (Config); + +} /* ReadPermConfig */ + + +/*---------------------------------------------------------------------------*/ +TEMP_CONFIG ReadTempConfig(FILE *File) { +/* + ** Parameters: + ** File open file to read temporary config from + ** Globals: none + ** Operation: Read a temporary configuration description from File + ** and return a ptr to it. + ** Return: Ptr to new temporary configuration description. + ** Exceptions: none + ** History: Tue Mar 19 14:29:59 1991, DSJ, Created. + */ + TEMP_CONFIG Config; + + Config = + (TEMP_CONFIG) c_alloc_struct (sizeof (TEMP_CONFIG_STRUCT), + "TEMP_CONFIG_STRUCT"); + fread ((char *) Config, sizeof (TEMP_CONFIG_STRUCT), 1, File); + + Config->Protos = NewBitVector (Config->ProtoVectorSize * BITSINLONG); + fread ((char *) Config->Protos, sizeof (UINT32), + Config->ProtoVectorSize, File); + + return (Config); + +} /* ReadTempConfig */ + + +/*---------------------------------------------------------------------------*/ +void WriteAdaptedClass(FILE *File, ADAPT_CLASS Class, int NumConfigs) { +/* + ** Parameters: + ** File open file to write Class to + ** Class adapted class to write to File + ** NumConfigs number of configs in Class + ** Globals: none + ** Operation: This routine writes a binary representation of Class + ** to File. + ** Return: none + ** Exceptions: none + ** History: Tue Mar 19 13:33:51 1991, DSJ, Created. + */ + int NumTempProtos; + LIST TempProtos; + int i; + + /* first write high level adapted class structure */ + fwrite ((char *) Class, sizeof (ADAPT_CLASS_STRUCT), 1, File); + + /* then write out the definitions of the permanent protos and configs */ + fwrite ((char *) Class->PermProtos, sizeof (UINT32), + WordsInVectorOfSize (MAX_NUM_PROTOS), File); + fwrite ((char *) Class->PermConfigs, sizeof (UINT32), + WordsInVectorOfSize (MAX_NUM_CONFIGS), File); + + /* then write out the list of temporary protos */ + NumTempProtos = count (Class->TempProtos); + fwrite ((char *) &NumTempProtos, sizeof (int), 1, File); + TempProtos = Class->TempProtos; + iterate (TempProtos) { + void* proto = first(TempProtos); + fwrite ((char *) proto, sizeof (TEMP_PROTO_STRUCT), 1, File); + } + + /* then write out the adapted configs */ + fwrite ((char *) &NumConfigs, sizeof (int), 1, File); + for (i = 0; i < NumConfigs; i++) + if (test_bit (Class->PermConfigs, i)) + WritePermConfig (File, Class->Config[i].Perm); + else + WriteTempConfig (File, Class->Config[i].Temp); + +} /* WriteAdaptedClass */ + + +/*---------------------------------------------------------------------------*/ +void WriteAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates) { +/* + ** Parameters: + ** File open text file to write Templates to + ** Templates set of adapted templates to write to File + ** Globals: none + ** Operation: This routine saves Templates to File in a binary format. + ** Return: none + ** Exceptions: none + ** History: Mon Mar 18 15:07:32 1991, DSJ, Created. + */ + int i; + + /* first write the high level adaptive template struct */ + fwrite ((char *) Templates, sizeof (ADAPT_TEMPLATES_STRUCT), 1, File); + + /* then write out the basic integer templates */ + WriteIntTemplates (File, Templates->Templates); + + /* then write out the adaptive info for each class */ + for (i = 0; i < NumClassesIn (Templates->Templates); i++) { + WriteAdaptedClass (File, Templates->Class[i], + NumIntConfigsIn (ClassForIndex + (Templates->Templates, i))); + } +} /* WriteAdaptedTemplates */ + + +/*---------------------------------------------------------------------------*/ +void WritePermConfig(FILE *File, PERM_CONFIG Config) { +/* + ** Parameters: + ** File open file to write Config to + ** Config permanent config to write to File + ** Globals: none + ** Operation: This routine writes a binary representation of a + ** permanent configuration to File. + ** Return: none + ** Exceptions: none + ** History: Tue Mar 19 13:55:44 1991, DSJ, Created. + */ + UINT8 NumAmbigs; + + assert (Config != NULL); + + NumAmbigs = strlen (Config); + fwrite ((char *) &NumAmbigs, sizeof (UINT8), 1, File); + fwrite (Config, sizeof (char), NumAmbigs, File); + +} /* WritePermConfig */ + + +/*---------------------------------------------------------------------------*/ +void WriteTempConfig(FILE *File, TEMP_CONFIG Config) { +/* + ** Parameters: + ** File open file to write Config to + ** Config temporary config to write to File + ** Globals: none + ** Operation: This routine writes a binary representation of a + ** temporary configuration to File. + ** Return: none + ** Exceptions: none + ** History: Tue Mar 19 14:00:28 1991, DSJ, Created. + */ + assert (Config != NULL); + /* contexts not yet implemented */ + assert (Config->ContextsSeen == NULL); + + fwrite ((char *) Config, sizeof (TEMP_CONFIG_STRUCT), 1, File); + fwrite ((char *) Config->Protos, sizeof (UINT32), + Config->ProtoVectorSize, File); + +} /* WriteTempConfig */ diff --git a/classify/adaptive.h b/classify/adaptive.h new file mode 100644 index 0000000000..17c8f52d95 --- /dev/null +++ b/classify/adaptive.h @@ -0,0 +1,199 @@ +/****************************************************************************** + ** Filename: adaptive.h + ** Purpose: Interface to adaptive matcher. + ** Author: Dan Johnson + ** History: Fri Mar 8 10:00:49 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef ADAPTIVE_H +#define ADAPTIVE_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "oldlist.h" +#include "intproto.h" +#include + +typedef struct +{ + UINT16 ProtoId; + UINT16 dummy; + PROTO_STRUCT Proto; +} + + +TEMP_PROTO_STRUCT; +typedef TEMP_PROTO_STRUCT *TEMP_PROTO; + +typedef struct +{ + UINT8 NumTimesSeen; + UINT8 ProtoVectorSize; + PROTO_ID MaxProtoId; + LIST ContextsSeen; + BIT_VECTOR Protos; +} TEMP_CONFIG_STRUCT; +typedef TEMP_CONFIG_STRUCT *TEMP_CONFIG; + +typedef char *PERM_CONFIG; + +typedef union +{ + TEMP_CONFIG Temp; + PERM_CONFIG Perm; +} ADAPTED_CONFIG; + +typedef struct +{ + UINT8 NumPermConfigs; + UINT8 dummy[3]; + BIT_VECTOR PermProtos; + BIT_VECTOR PermConfigs; + LIST TempProtos; + ADAPTED_CONFIG Config[MAX_NUM_CONFIGS]; +} ADAPT_CLASS_STRUCT; +typedef ADAPT_CLASS_STRUCT *ADAPT_CLASS; + +typedef struct +{ + INT_TEMPLATES Templates; + UINT8 NumPermClasses; + UINT8 dummy[3]; + ADAPT_CLASS Class[MAX_NUM_CLASSES]; +} ADAPT_TEMPLATES_STRUCT; +typedef ADAPT_TEMPLATES_STRUCT *ADAPT_TEMPLATES; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +#define ConfigIsPermanent(Class,ConfigId) \ +(test_bit ((Class)->PermConfigs, ConfigId)) + +#define MakeConfigPermanent(Class,ConfigId) \ +(SET_BIT ((Class)->PermConfigs, ConfigId)) + +#define MakeProtoPermanent(Class,ProtoId) \ +(SET_BIT ((Class)->PermProtos, ProtoId)) + +#define TempConfigFor(Class,ConfigId) \ +((Class)->Config[ConfigId].Temp) + +#define PermConfigFor(Class,ConfigId) \ +((Class)->Config[ConfigId].Perm) + +#define IncreaseConfidence(TempConfig) \ +((TempConfig)->NumTimesSeen++) + +int AddAdaptedClass(ADAPT_TEMPLATES Templates, + ADAPT_CLASS Class, + CLASS_ID ClassId); + +void FreeTempProto(void *arg); + +void FreeTempConfig(TEMP_CONFIG Config); + +ADAPT_CLASS NewAdaptedClass(); + +void free_adapted_class(ADAPT_CLASS adapt_class); + +ADAPT_TEMPLATES NewAdaptedTemplates(); + +void free_adapted_templates(ADAPT_TEMPLATES templates); + +TEMP_CONFIG NewTempConfig(int MaxProtoId); + +TEMP_PROTO NewTempProto(); + +void PrintAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates); + +ADAPT_CLASS ReadAdaptedClass(FILE *File); + +ADAPT_TEMPLATES ReadAdaptedTemplates(FILE *File); + +PERM_CONFIG ReadPermConfig(FILE *File); + +TEMP_CONFIG ReadTempConfig(FILE *File); + +void WriteAdaptedClass(FILE *File, ADAPT_CLASS Class, int NumConfigs); + +void WriteAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates); + +void WritePermConfig(FILE *File, PERM_CONFIG Config); + +void WriteTempConfig(FILE *File, TEMP_CONFIG Config); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* adaptive.c +int AddAdaptedClass + _ARGS((ADAPT_TEMPLATES Templates, + ADAPT_CLASS Class, + CLASS_ID ClassId)); + +void FreeTempConfig + _ARGS((TEMP_CONFIG Config)); + +ADAPT_CLASS NewAdaptedClass + _ARGS((void)); + +ADAPT_TEMPLATES NewAdaptedTemplates + _ARGS((void)); + +TEMP_CONFIG NewTempConfig + _ARGS((int MaxProtoId)); + +TEMP_PROTO NewTempProto + _ARGS((void)); + +void PrintAdaptedTemplates + _ARGS((FILE *File, + ADAPT_TEMPLATES Templates)); + +ADAPT_CLASS ReadAdaptedClass + _ARGS((FILE *File)); + +ADAPT_TEMPLATES ReadAdaptedTemplates + _ARGS((FILE *File)); + +PERM_CONFIG ReadPermConfig + _ARGS((FILE *File)); + +TEMP_CONFIG ReadTempConfig + _ARGS((FILE *File)); + +void WriteAdaptedClass + _ARGS((FILE *File, + ADAPT_CLASS Class, + int NumConfigs)); + +void WriteAdaptedTemplates + _ARGS((FILE *File, + ADAPT_TEMPLATES Templates)); + +void WritePermConfig + _ARGS((FILE *File, + PERM_CONFIG Config)); + +void WriteTempConfig + _ARGS((FILE *File, + TEMP_CONFIG Config)); + +#undef _ARGS +*/ +#endif diff --git a/classify/adaptmatch.cpp b/classify/adaptmatch.cpp new file mode 100644 index 0000000000..7602e2e8fe --- /dev/null +++ b/classify/adaptmatch.cpp @@ -0,0 +1,3247 @@ +/****************************************************************************** + ** Filename: adaptmatch.c + ** Purpose: High level adaptive matcher. + ** Author: Dan Johnson + ** History: Mon Mar 11 10:00:10 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include +#include "adaptmatch.h" +#include "normfeat.h" +#include "mfoutline.h" +#include "picofeat.h" +#include "float2int.h" +#include "outfeat.h" +#include "emalloc.h" +#include "intfx.h" +#include "permnum.h" +#include "speckle.h" +#include "efio.h" +#include "normmatch.h" +#include "stopper.h" +#include "permute.h" +#include "context.h" +#include "ndminx.h" +#include "intproto.h" +#include "const.h" +#include "globals.h" +#include "werd.h" +#include "callcpp.h" +#include "tordvars.h" + +#include +#include +#include +#include +#include +#ifdef __UNIX__ +#include +#endif + +#define ADAPT_TEMPLATE_SUFFIX ".a" +#define BUILT_IN_TEMPLATES_FILE "tessdata/inttemp" +#define BUILT_IN_CUTOFFS_FILE "tessdata/pffmtable" + +#define MAX_MATCHES 10 +#define UNLIKELY_NUM_FEAT 200 +#define NO_DEBUG 0 +#define MAX_ADAPTABLE_WERD_SIZE 40 +#define ADAPTABLE_WERD (GOOD_NUMBER + 0.05) + +#define Y_DIM_OFFSET (Y_SHIFT - BASELINE_Y_SHIFT) + +#define WORST_POSSIBLE_RATING (1.0) + +typedef struct +{ + FLOAT32 BlobLength; + int NumMatches; + CLASS_ID Classes[MAX_NUM_CLASSES]; + FLOAT32 Ratings[MAX_CLASS_ID + 1]; + UINT8 Configs[MAX_CLASS_ID + 1]; + FLOAT32 BestRating; + CLASS_ID BestClass; + UINT8 BestConfig; +} + + +ADAPT_RESULTS; + +typedef struct +{ + ADAPT_TEMPLATES Templates; + CLASS_ID ClassId; + int ConfigId; +} + + +PROTO_KEY; + +/**---------------------------------------------------------------------------- + Private Macros +----------------------------------------------------------------------------**/ +#define MarginalMatch(Rating) \ +((Rating) > GreatAdaptiveMatch) + +#define TempConfigReliable(Config) \ +((Config)->NumTimesSeen > ReliableConfigThreshold) + +#define InitIntFX() (FeaturesHaveBeenExtracted = FALSE) + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +void AdaptToChar(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold); + +void AdaptToPunc(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold); + +void AddNewResult(ADAPT_RESULTS *Results, + CLASS_ID ClassId, + FLOAT32 Rating, + int ConfigId); + +void AmbigClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + char *Ambiguities, + ADAPT_RESULTS *Results); + +char *BaselineClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_TEMPLATES Templates, + ADAPT_RESULTS *Results); + +void make_config_pruner(INT_TEMPLATES templates, CONFIG_PRUNER *config_pruner); + +void CharNormClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + ADAPT_RESULTS *Results); + +void ClassifyAsNoise(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results); + + //CLASS_ID *Class1, +int CompareCurrentRatings(const void *arg1, + const void *arg2); //CLASS_ID *Class2); + +LIST ConvertMatchesToChoices(ADAPT_RESULTS *Results); + +void DebugAdaptiveClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results); + +void DoAdaptiveMatch(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results); + +void GetAdaptThresholds (TWERD * Word, +LINE_STATS * LineStats, +const char *BestChoice, +const char *BestRawChoice, FLOAT32 Thresholds[]); + +char *GetAmbiguities(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID CorrectClass); + +int GetBaselineFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength); + +FLOAT32 GetBestRatingFor(TBLOB *Blob, LINE_STATS *LineStats, CLASS_ID ClassId); + +int GetCharNormFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength); + +int GetIntBaselineFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength); + +int GetIntCharNormFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength); + +void InitMatcherRatings(register FLOAT32 *Rating); + +void MakeNewTemporaryConfig(ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int NumFeatures, + INT_FEATURE_ARRAY Features, + FEATURE_SET FloatFeatures); + +PROTO_ID MakeNewTempProtos (FEATURE_SET Features, +int NumBadFeat, +FEATURE_ID BadFeat[], +INT_CLASS IClass, +ADAPT_CLASS Class, BIT_VECTOR TempProtoMask); + +void MakePermanent(ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int ConfigId, + TBLOB *Blob, + LINE_STATS *LineStats); + +int MakeTempProtoPerm(void *item1, void *item2); + +int NumBlobsIn(TWERD *Word); + +int NumOutlinesInBlob(TBLOB *Blob); + +void PrintAdaptiveMatchResults(FILE *File, ADAPT_RESULTS *Results); + +void RemoveBadMatches(ADAPT_RESULTS *Results); + +void RemoveExtraPuncs(ADAPT_RESULTS *Results); + +void SetAdaptiveThreshold(FLOAT32 Threshold); +void ShowBestMatchFor(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + BOOL8 AdaptiveOn, + BOOL8 PreTrainedOn); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/adaptmatch.c +int AdaptableWord + _ARGS((TWERD *Word, + char *BestChoice, + char *BestRawChoice)); + + void AdaptToChar + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold)); + + void AdaptToPunc + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold)); + + void AddNewResult + _ARGS((ADAPT_RESULTS *Results, + CLASS_ID ClassId, + FLOAT32 Rating, + int ConfigId)); + + void AmbigClassifier + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + char *Ambiguities, + ADAPT_RESULTS *Results)); + + char *BaselineClassifier + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + ADAPT_TEMPLATES Templates, + ADAPT_RESULTS *Results)); + + void CharNormClassifier + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + ADAPT_RESULTS *Results)); + + void ClassifyAsNoise + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results)); + + int CompareCurrentRatings + _ARGS((CLASS_ID *Class1, + CLASS_ID *Class2)); + + LIST ConvertMatchesToChoices + _ARGS((ADAPT_RESULTS *Results)); + + void DebugAdaptiveClassifier + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results)); + + void DoAdaptiveMatch + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results)); + + void GetAdaptThresholds + _ARGS((TWERD *Word, + LINE_STATS *LineStats, + char *BestChoice, + char *BestRawChoice, + FLOAT32 Thresholds [])); + +int GetAdaptiveFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_FEATURE_ARRAY IntFeatures, + CHAR_DESC *FloatFeatures)); + + char *GetAmbiguities + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID CorrectClass)); + + int GetBaselineFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength)); + + FLOAT32 GetBestRatingFor + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId)); + + int GetCharNormFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength)); + + int GetIntBaselineFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength)); + + int GetIntCharNormFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength)); + + void InitMatcherRatings + _ARGS((FLOAT32 *Rating)); + +void MakeNewAdaptedClass + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + ADAPT_TEMPLATES Templates)); + + void MakeNewTemporaryConfig + _ARGS((ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int NumFeatures, + INT_FEATURE_ARRAY Features, + FEATURE_SET FloatFeatures)); + + PROTO_ID MakeNewTempProtos + _ARGS((FEATURE_SET Features, + int NumBadFeat, + FEATURE_ID BadFeat [], + INT_CLASS IClass, + ADAPT_CLASS Class, + BIT_VECTOR TempProtoMask)); + + void MakePermanent + _ARGS((ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int ConfigId, + BLOB *Blob, + LINE_STATS *LineStats)); + + int MakeTempProtoPerm + _ARGS((TEMP_PROTO TempProto, + PROTO_KEY *ProtoKey)); + + int NumBlobsIn + _ARGS((TWERD *Word)); + + int NumOutlinesInBlob + _ARGS((BLOB *Blob)); + + void PrintAdaptiveMatchResults + _ARGS((FILE *File, + ADAPT_RESULTS *Results)); + + void RemoveBadMatches + _ARGS((ADAPT_RESULTS *Results)); + void RemoveExtraPuncs + _ARGS((ADAPT_RESULTS *Results)); + + void SetAdaptiveThreshold + _ARGS((FLOAT32 Threshold)); + + void ShowBestMatchFor + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + BOOL8 AdaptiveOn, + BOOL8 PreTrainedOn)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* name of current image file being processed */ +extern char imagefile[]; +//extern char *demodir; +INT_VAR (tessedit_single_match, FALSE, "Top choice only from CP"); + +//extern "C" int il1_adaption_test; //? +//extern int display_ratings; +//extern "C" int newcp_ratings_on; +//extern int config_pruner_enabled; +//extern "C" int feature_prune_percentile; +//extern "C" double newcp_duff_rating; +/* variables used to hold performance statistics */ +static int AdaptiveMatcherCalls = 0; +static int BaselineClassifierCalls = 0; +static int CharNormClassifierCalls = 0; +static int AmbigClassifierCalls = 0; +static int NumWordsAdaptedTo = 0; +static int NumCharsAdaptedTo = 0; +static int NumBaselineClassesTried = 0; +static int NumCharNormClassesTried = 0; +static int NumAmbigClassesTried = 0; +static int NumClassesOutput = 0; + +/* define globals used to hold onto extracted features. This is used +to map from the old scheme in which baseline features and char norm +features are extracted separately, to the new scheme in which they +are extracted at the same time. */ +static BOOL8 FeaturesHaveBeenExtracted = FALSE; +static BOOL8 FeaturesOK = TRUE; +static INT_FEATURE_ARRAY BaselineFeatures; +static INT_FEATURE_ARRAY CharNormFeatures; +//static CLASS_NORMALIZATION_ARRAY +// NormalizationAdjustments; +static INT_FX_RESULT_STRUCT FXInfo; + +/* use a global variable to hold onto the current ratings so that the +comparison function passes to qsort can get at them */ +static FLOAT32 *CurrentRatings; + +/* define globals to hold filenames of training data */ +static const char *BuiltInTemplatesFile = BUILT_IN_TEMPLATES_FILE; +static const char *BuiltInCutoffsFile = BUILT_IN_CUTOFFS_FILE; +static CLASS_CUTOFF_ARRAY CharNormCutoffs; +static CLASS_CUTOFF_ARRAY BaselineCutoffs; + +/* use global variables to hold onto built-in templates and adapted +templates */ +static INT_TEMPLATES PreTrainedTemplates; +static ADAPT_TEMPLATES AdaptedTemplates; + +/* create dummy proto and config masks for use with the built-in templates */ +static BIT_VECTOR AllProtosOn; +static BIT_VECTOR PrunedProtos; +static BIT_VECTOR AllConfigsOn; +static BIT_VECTOR AllProtosOff; +static BIT_VECTOR AllConfigsOff; +static BIT_VECTOR TempProtoMask; + +/* define control knobs for adaptive matcher */ +make_toggle_const (EnableAdaptiveMatcher, 1, MakeEnableAdaptiveMatcher); +/* PREV DEFAULT 0 */ + +make_toggle_const (UsePreAdaptedTemplates, 0, MakeUsePreAdaptedTemplates); +make_toggle_const (SaveAdaptedTemplates, 0, MakeSaveAdaptedTemplates); + +make_toggle_var (EnableAdaptiveDebugger, 0, MakeEnableAdaptiveDebugger, +18, 1, SetEnableAdaptiveDebugger, "Enable match debugger"); + +make_int_var (MatcherDebugLevel, 0, MakeMatcherDebugLevel, +18, 2, SetMatcherDebugLevel, "Matcher Debug Level: "); + +make_int_var (MatchDebugFlags, 0, MakeMatchDebugFlags, +18, 3, SetMatchDebugFlags, "Matcher Debug Flags: "); + +make_toggle_var (EnableLearning, 1, MakeEnableLearning, +18, 4, SetEnableLearning, "Enable learning"); +/* PREV DEFAULT 0 */ + /*record it for multiple pages */ +static int old_enable_learning = 1; + +make_int_var (LearningDebugLevel, 0, MakeLearningDebugLevel, +18, 5, SetLearningDebugLevel, "Learning Debug Level: "); + +make_float_var (GoodAdaptiveMatch, 0.125, MakeGoodAdaptiveMatch, +18, 6, SetGoodAdaptiveMatch, "Good Match (0-1): "); + +make_float_var (GreatAdaptiveMatch, 0.0, MakeGreatAdaptiveMatch, +18, 7, SetGreatAdaptiveMatch, "Great Match (0-1): "); +/* PREV DEFAULT 0.10 */ + +make_float_var (PerfectRating, 0.02, MakePerfectRating, +18, 8, SetPerfectRating, "Perfect Match (0-1): "); + +make_float_var (BadMatchPad, 0.15, MakeBadMatchPad, +18, 9, SetBadMatchPad, "Bad Match Pad (0-1): "); + +make_float_var (RatingMargin, 0.1, MakeRatingMargin, +18, 10, SetRatingMargin, "New template margin (0-1): "); + +make_float_var (NoiseBlobLength, 0.6, MakeNoiseBlobLength, +18, 11, SetNoiseBlobLength, "Avg. noise blob length: "); + +make_int_var (MinNumPermClasses, 3, MakeMinNumPermClasses, +18, 12, SetMinNumPermClasses, "Min # of permanent classes: "); +/* PREV DEFAULT 200 */ + +make_int_var (ReliableConfigThreshold, 2, MakeReliableConfigThreshold, +18, 13, SetReliableConfigThreshold, +"Reliable Config Threshold: "); + +make_float_var (MaxAngleDelta, 0.015, MakeMaxAngleDelta, +18, 14, SetMaxAngleDelta, +"Maximum angle delta for proto clustering: "); + +make_toggle_var (EnableIntFX, 1, MakeEnableIntFX, +18, 15, SetEnableIntFX, "Enable integer fx"); +/* PREV DEFAULT 0 */ + +make_toggle_var (EnableNewAdaptRules, 1, MakeEnableNewAdaptRules, +18, 16, SetEnableNewAdaptRules, +"Enable new adaptation rules"); +/* PREV DEFAULT 0 */ + +make_float_var (RatingScale, 30.0, MakeRatingScale, +18, 17, SetRatingScale, "Rating scale: "); + +make_float_var (CertaintyScale, 20.0, MakeCertaintyScale, +18, 18, SetCertaintyScale, "CertaintyScale: "); + +int tess_cn_matching = 0; +int tess_bn_matching = 0; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +LIST AdaptiveClassifier(TBLOB *Blob, TBLOB *DotBlob, TEXTROW *Row) { +/* + ** Parameters: + ** Blob blob to be classified + ** DotBlob (obsolete) + ** Row row of text that word appears in + ** Globals: + ** CurrentRatings + used by compare function for qsort +** Operation: This routine calls the adaptive matcher which returns +** (in an array) the class id of each class matched. It also +** returns the number of classes matched. +** For each class matched it places the best rating +** found for that class into the Ratings array. +** Bad matches are then removed so that they don't need to be +** sorted. The remaining good matches are then sorted and +** converted to choices. +** This routine also performs some simple speckle filtering. +** Return: List of choices found by adaptive matcher. +** Exceptions: none +** History: Mon Mar 11 10:00:58 1991, DSJ, Created. +*/ + LIST Choices; + ADAPT_RESULTS Results; + LINE_STATS LineStats; + + if (AdaptedTemplates == NULL) + AdaptedTemplates = NewAdaptedTemplates (); + EnterClassifyMode; + + Results.BlobLength = MAX_FLOAT32; + Results.NumMatches = 0; + Results.BestRating = WORST_POSSIBLE_RATING; + Results.BestClass = NO_CLASS; + Results.BestConfig = 0; + GetLineStatsFromRow(Row, &LineStats); + InitMatcherRatings (Results.Ratings); + + DoAdaptiveMatch(Blob, &LineStats, &Results); + RemoveBadMatches(&Results); + + /* save ratings in a global so that CompareCurrentRatings() can see them */ + CurrentRatings = Results.Ratings; + qsort ((void *) (Results.Classes), Results.NumMatches, + sizeof (CLASS_ID), CompareCurrentRatings); + RemoveExtraPuncs(&Results); + Choices = ConvertMatchesToChoices (&Results); + + if (MatcherDebugLevel >= 1) { + cprintf ("AD Matches = "); + PrintAdaptiveMatchResults(stdout, &Results); + } + + if (LargeSpeckle (Blob, Row)) + Choices = AddLargeSpeckleTo (Choices); + +#ifndef GRAPHICS_DISABLED + if (EnableAdaptiveDebugger) + DebugAdaptiveClassifier(Blob, &LineStats, &Results); +#endif + + NumClassesOutput += count (Choices); + if (Choices == NIL) { + if (!bln_numericmode) + printf ("Nil classification!\n"); // Should never normally happen. + return (append_choice (NIL, "", 50.0f, -20.0f, -1)); + } + + return (Choices); + +} /* AdaptiveClassifier */ + + +/*---------------------------------------------------------------------------*/ +void AdaptToWord(TWERD *Word, + TEXTROW *Row, + const char *BestChoice, + const char *BestRawChoice, + const char *rejmap) { +/* + ** Parameters: + ** Word + word to be adapted to +** Row + row of text that word is found in +** BestChoice + best choice for word found by system +** BestRawChoice + best choice for word found by classifier only +** Globals: +** EnableLearning + TRUE if learning is enabled +** Operation: This routine implements a preliminary version of the +** rules which are used to decide which characters to adapt to. +** A word is adapted to if it is in the dictionary or if it +** is a "good" number (no trailing units, etc.). It cannot +** contain broken or merged characters. Within that word, only +** letters and digits are adapted to (no punctuation). +** Return: none +** Exceptions: none +** History: Thu Mar 14 07:40:36 1991, DSJ, Created. +*/ + TBLOB *Blob; + LINE_STATS LineStats; + FLOAT32 Thresholds[MAX_ADAPTABLE_WERD_SIZE]; + FLOAT32 *Threshold; + const char *map = rejmap; + char map_char = '1'; + + if (EnableLearning) { + NumWordsAdaptedTo++; + + #ifndef SECURE_NAMES + if (LearningDebugLevel >= 1) + cprintf ("\n\nAdapting to word = %s\n", BestChoice); + #endif + GetLineStatsFromRow(Row, &LineStats); + + GetAdaptThresholds(Word, + &LineStats, + BestChoice, + BestRawChoice, + Thresholds); + + for (Blob = Word->blobs, Threshold = Thresholds; + Blob != NULL; Blob = Blob->next, BestChoice++, Threshold++) { + InitIntFX(); + + if (rejmap != NULL) + map_char = *map++; + + assert (map_char == '1' || map_char == '0'); + + if (map_char == '1') { + + if (isalnum (*BestChoice)) { + /* SPECIAL RULE: don't adapt to an 'i' which is the first char + in a word because they are too ambiguous with 'I'. + The new adaptation rules should account for this + automatically, since they exclude ambiguous words from + adaptation, but for safety's sake we'll leave the rule in. + Also, don't adapt to i's that have only 1 blob in them + because this creates too much ambiguity for broken + characters. */ + if ((*BestChoice == 'i' + || il1_adaption_test && *BestChoice == 'I' + && islower (BestChoice[1])) && (Blob == Word->blobs + || + ispunct (* + (BestChoice - + 1)) + || !il1_adaption_test + && + NumOutlinesInBlob + (Blob) != 2)) { + if (LearningDebugLevel >= 1) + cprintf ("Rejecting char = %c\n", *BestChoice); + } + else { + #ifndef SECURE_NAMES + if (LearningDebugLevel >= 1) + cprintf ("Adapting to char = %c, thr= %g\n", *BestChoice, *Threshold); + #endif + AdaptToChar(Blob, &LineStats, *BestChoice, *Threshold); + } + } + else + AdaptToPunc(Blob, &LineStats, *BestChoice, *Threshold); + } + } + if (LearningDebugLevel >= 1) + cprintf ("\n"); + } +} /* AdaptToWord */ + + +/*---------------------------------------------------------------------------*/ +void EndAdaptiveClassifier() { +/* + ** Parameters: none + ** Globals: + ** AdaptedTemplates + current set of adapted templates +** SaveAdaptedTemplates + TRUE if templates should be saved +** EnableAdaptiveMatcher + TRUE if adaptive matcher is enabled +** Operation: This routine performs cleanup operations on the +** adaptive classifier. It should be called before the +** program is terminated. Its main function is to save +** the adapted templates to a file. +** Return: none +** Exceptions: none +** History: Tue Mar 19 14:37:06 1991, DSJ, Created. +*/ + char Filename[256]; + FILE *File; + + #ifndef SECURE_NAMES + if (EnableAdaptiveMatcher && SaveAdaptedTemplates) { + strcpy(Filename, imagefile); + strcat(Filename, ADAPT_TEMPLATE_SUFFIX); + File = fopen (Filename, "wb"); + if (File == NULL) + cprintf ("Unable to save adapted templates to %s!\n", Filename); + else { + cprintf ("\nSaving adapted templates to %s ...", Filename); + fflush(stdout); + WriteAdaptedTemplates(File, AdaptedTemplates); + cprintf ("\n"); + fclose(File); + } + } + #endif + EndDangerousAmbigs(); + FreeNormProtos(); + free_int_templates(PreTrainedTemplates); + PreTrainedTemplates = NULL; + FreeBitVector(AllProtosOn); + FreeBitVector(PrunedProtos); + FreeBitVector(AllConfigsOn); + FreeBitVector(AllProtosOff); + FreeBitVector(AllConfigsOff); + FreeBitVector(TempProtoMask); + AllProtosOn = NULL; + PrunedProtos = NULL; + AllConfigsOn = NULL; + AllProtosOff = NULL; + AllConfigsOff = NULL; + TempProtoMask = NULL; +} /* EndAdaptiveClassifier */ + + +/*---------------------------------------------------------------------------*/ +void InitAdaptiveClassifier() { +/* + ** Parameters: none + ** Globals: + ** BuiltInTemplatesFile + file to get built-in temps from +** BuiltInCutoffsFile + file to get avg. feat per class from +** PreTrainedTemplates + pre-trained configs and protos +** AdaptedTemplates + templates adapted to current page +** CharNormCutoffs + avg # of features per class +** AllProtosOn + dummy proto mask with all bits 1 +** AllConfigsOn + dummy config mask with all bits 1 +** UsePreAdaptedTemplates + enables use of pre-adapted templates +** Operation: This routine reads in the training information needed +** by the adaptive classifier and saves it into global +** variables. +** Return: none +** Exceptions: none +** History: Mon Mar 11 12:49:34 1991, DSJ, Created. +*/ + int i; + FILE *File; + char Filename[1024]; + + if (!EnableAdaptiveMatcher) + return; + + strcpy(Filename, demodir); + strcat(Filename, BuiltInTemplatesFile); + #ifndef SECURE_NAMES + // cprintf( "\nReading built-in templates from %s ...", + // Filename); + fflush(stdout); + #endif + + #ifdef __UNIX__ + File = Efopen (Filename, "r"); + #else + File = Efopen (Filename, "rb"); + #endif + PreTrainedTemplates = ReadIntTemplates (File, TRUE); + fclose(File); + + strcpy(Filename, demodir); + strcat(Filename, BuiltInCutoffsFile); + #ifndef SECURE_NAMES + // cprintf( "\nReading built-in pico-feature cutoffs from %s ...", + // Filename); + fflush(stdout); + #endif + ReadNewCutoffs (Filename, PreTrainedTemplates->IndexFor, CharNormCutoffs); + + GetNormProtos(); + + InitIntegerMatcher(); + InitIntegerFX(); + + AllProtosOn = NewBitVector (MAX_NUM_PROTOS); + PrunedProtos = NewBitVector (MAX_NUM_PROTOS); + AllConfigsOn = NewBitVector (MAX_NUM_CONFIGS); + AllProtosOff = NewBitVector (MAX_NUM_PROTOS); + AllConfigsOff = NewBitVector (MAX_NUM_CONFIGS); + TempProtoMask = NewBitVector (MAX_NUM_PROTOS); + set_all_bits (AllProtosOn, WordsInVectorOfSize (MAX_NUM_PROTOS)); + set_all_bits (PrunedProtos, WordsInVectorOfSize (MAX_NUM_PROTOS)); + set_all_bits (AllConfigsOn, WordsInVectorOfSize (MAX_NUM_CONFIGS)); + zero_all_bits (AllProtosOff, WordsInVectorOfSize (MAX_NUM_PROTOS)); + zero_all_bits (AllConfigsOff, WordsInVectorOfSize (MAX_NUM_CONFIGS)); + + if (UsePreAdaptedTemplates) { + strcpy(Filename, imagefile); + strcat(Filename, ADAPT_TEMPLATE_SUFFIX); + File = fopen (Filename, "rb"); + if (File == NULL) + AdaptedTemplates = NewAdaptedTemplates (); + else { + #ifndef SECURE_NAMES + cprintf ("\nReading pre-adapted templates from %s ...", Filename); + fflush(stdout); + #endif + AdaptedTemplates = ReadAdaptedTemplates (File); + cprintf ("\n"); + fclose(File); + PrintAdaptedTemplates(stdout, AdaptedTemplates); + + for (i = 0; i < NumClassesIn (AdaptedTemplates->Templates); i++) { + BaselineCutoffs[i] = + CharNormCutoffs[IndexForClassId (PreTrainedTemplates, + ClassIdForIndex + (AdaptedTemplates->Templates, + i))]; + } + } + } + else + AdaptedTemplates = NewAdaptedTemplates (); + old_enable_learning = EnableLearning; + +} /* InitAdaptiveClassifier */ + +void ResetAdaptiveClassifier() { + free_adapted_templates(AdaptedTemplates); + AdaptedTemplates = NULL; +} + + +/*---------------------------------------------------------------------------*/ +void InitAdaptiveClassifierVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine installs the control knobs used by the + ** adaptive matcher. + ** Return: none + ** Exceptions: none + ** History: Mon Mar 11 12:49:34 1991, DSJ, Created. + */ + VALUE dummy; + + string_variable (BuiltInTemplatesFile, "BuiltInTemplatesFile", + BUILT_IN_TEMPLATES_FILE); + string_variable (BuiltInCutoffsFile, "BuiltInCutoffsFile", + BUILT_IN_CUTOFFS_FILE); + + MakeEnableAdaptiveMatcher(); + MakeUsePreAdaptedTemplates(); + MakeSaveAdaptedTemplates(); + + MakeEnableLearning(); + MakeEnableAdaptiveDebugger(); + MakeBadMatchPad(); + MakeGoodAdaptiveMatch(); + MakeGreatAdaptiveMatch(); + MakeNoiseBlobLength(); + MakeMinNumPermClasses(); + MakeReliableConfigThreshold(); + MakeMaxAngleDelta(); + MakeLearningDebugLevel(); + MakeMatcherDebugLevel(); + MakeMatchDebugFlags(); + MakeRatingMargin(); + MakePerfectRating(); + MakeEnableIntFX(); + MakeEnableNewAdaptRules(); + MakeRatingScale(); + MakeCertaintyScale(); + + InitPicoFXVars(); + InitOutlineFXVars(); //? + +} /* InitAdaptiveClassifierVars */ + + +/*---------------------------------------------------------------------------*/ +void PrintAdaptiveStatistics(FILE *File) { +/* + ** Parameters: + ** File + open text file to print adaptive statistics to +** Globals: none +** Operation: Print to File the statistics which have been gathered +** for the adaptive matcher. +** Return: none +** Exceptions: none +** History: Thu Apr 18 14:37:37 1991, DSJ, Created. +*/ + #ifndef SECURE_NAMES + + fprintf (File, "\nADAPTIVE MATCHER STATISTICS:\n"); + fprintf (File, "\tNum blobs classified = %d\n", AdaptiveMatcherCalls); + fprintf (File, "\tNum classes output = %d (Avg = %4.2f)\n", + NumClassesOutput, + ((AdaptiveMatcherCalls == 0) ? (0.0) : + ((float) NumClassesOutput / AdaptiveMatcherCalls))); + fprintf (File, "\t\tBaseline Classifier: %4d calls (%4.2f classes/call)\n", + BaselineClassifierCalls, + ((BaselineClassifierCalls == 0) ? (0.0) : + ((float) NumBaselineClassesTried / BaselineClassifierCalls))); + fprintf (File, "\t\tCharNorm Classifier: %4d calls (%4.2f classes/call)\n", + CharNormClassifierCalls, + ((CharNormClassifierCalls == 0) ? (0.0) : + ((float) NumCharNormClassesTried / CharNormClassifierCalls))); + fprintf (File, "\t\tAmbig Classifier: %4d calls (%4.2f classes/call)\n", + AmbigClassifierCalls, + ((AmbigClassifierCalls == 0) ? (0.0) : + ((float) NumAmbigClassesTried / AmbigClassifierCalls))); + + fprintf (File, "\nADAPTIVE LEARNER STATISTICS:\n"); + fprintf (File, "\tNumber of words adapted to: %d\n", NumWordsAdaptedTo); + fprintf (File, "\tNumber of chars adapted to: %d\n", NumCharsAdaptedTo); + + PrintAdaptedTemplates(File, AdaptedTemplates); + #endif +} /* PrintAdaptiveStatistics */ + + +/*---------------------------------------------------------------------------*/ +void SettupPass1() { +/* + ** Parameters: none + ** Globals: + ** EnableLearning + set to TRUE by this routine +** Operation: This routine prepares the adaptive matcher for the start +** of the first pass. Learning is enabled (unless it is +** disabled for the whole program). +** Return: none +** Exceptions: none +** History: Mon Apr 15 16:39:29 1991, DSJ, Created. +*/ + /* Note: this is somewhat redundant, it simply says that if learning is + enabled then it will remain enabled on the first pass. If it is + disabled, then it will remain disabled. This is only put here to + make it very clear that learning is controlled directly by the global + setting of EnableLearning. */ + EnableLearning = old_enable_learning; + + SettupStopperPass1(); + +} /* SettupPass1 */ + + +/*---------------------------------------------------------------------------*/ +void SettupPass2() { +/* + ** Parameters: none + ** Globals: + ** EnableLearning + set to FALSE by this routine +** Operation: This routine prepares the adaptive matcher for the start +** of the second pass. Further learning is disabled. +** Return: none +** Exceptions: none +** History: Mon Apr 15 16:39:29 1991, DSJ, Created. +*/ + EnableLearning = FALSE; + SettupStopperPass2(); + +} /* SettupPass2 */ + + +/*---------------------------------------------------------------------------*/ +void MakeNewAdaptedClass(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + ADAPT_TEMPLATES Templates) { +/* + ** Parameters: + ** Blob + blob to model new class after +** LineStats + statistics for text row blob is in +** ClassId + id of new class to be created +** Templates + adapted templates to add new class to +** Globals: +** AllProtosOn + dummy mask with all 1's +** BaselineCutoffs + kludge needed to get cutoffs +** PreTrainedTemplates + kludge needed to get cutoffs +** Operation: This routine creates a new adapted class and uses Blob +** as the model for the first config in that class. +** Return: none +** Exceptions: none +** History: Thu Mar 14 12:49:39 1991, DSJ, Created. +*/ + FEATURE_SET Features; + int Fid, Pid; + FEATURE Feature; + int NumFeatures; + TEMP_PROTO TempProto; + PROTO Proto; + ADAPT_CLASS Class; + INT_CLASS IClass; + CLASS_INDEX ClassIndex; + TEMP_CONFIG Config; + + NormMethod = baseline; + Features = ExtractOutlineFeatures (Blob, LineStats); + NumFeatures = NumFeaturesIn (Features); + if (NumFeatures > UNLIKELY_NUM_FEAT) { + FreeFeatureSet(Features); + return; + } + + Class = NewAdaptedClass (); + ClassIndex = AddAdaptedClass (Templates, Class, ClassId); + Config = NewTempConfig (NumFeatures - 1); + TempConfigFor (Class, 0) = Config; + + /* this is a kludge to construct cutoffs for adapted templates */ + BaselineCutoffs[ClassIndex] = + CharNormCutoffs[IndexForClassId (PreTrainedTemplates, ClassId)]; + + IClass = ClassForClassId (Templates->Templates, ClassId); + + for (Fid = 0; Fid < NumFeaturesIn (Features); Fid++) { + Pid = AddIntProto (IClass); + assert (Pid != NO_PROTO); + + Feature = FeatureIn (Features, Fid); + TempProto = NewTempProto (); + Proto = &(TempProto->Proto); + + /* compute proto params - NOTE that Y_DIM_OFFSET must be used because + ConvertProto assumes that the Y dimension varies from -0.5 to 0.5 + instead of the -0.25 to 0.75 used in baseline normalization */ + ProtoAngle (Proto) = ParamOf (Feature, OutlineFeatDir); + ProtoX (Proto) = ParamOf (Feature, OutlineFeatX); + ProtoY (Proto) = ParamOf (Feature, OutlineFeatY) - Y_DIM_OFFSET; + ProtoLength (Proto) = ParamOf (Feature, OutlineFeatLength); + FillABC(Proto); + + TempProto->ProtoId = Pid; + SET_BIT (Config->Protos, Pid); + + ConvertProto(Proto, Pid, IClass); + AddProtoToProtoPruner(Proto, Pid, IClass); + + Class->TempProtos = push (Class->TempProtos, TempProto); + } + FreeFeatureSet(Features); + + AddIntConfig(IClass); + ConvertConfig (AllProtosOn, 0, IClass); + + if (LearningDebugLevel >= 1) { + cprintf ("Added new class '%c' with index %d and %d protos.\n", + ClassId, ClassIndex, NumFeatures); + } + +} /* MakeNewAdaptedClass */ + + +/*---------------------------------------------------------------------------*/ +int GetAdaptiveFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_FEATURE_ARRAY IntFeatures, + FEATURE_SET *FloatFeatures) { +/* + ** Parameters: + ** Blob + blob to extract features from +** LineStats + statistics about text row blob is in +** IntFeatures + array to fill with integer features +** FloatFeatures + place to return actual floating-pt features +** Globals: none +** Operation: This routine sets up the feature extractor to extract +** baseline normalized pico-features. +** The extracted pico-features are converted +** to integer form and placed in IntFeatures. The original +** floating-pt. features are returned in FloatFeatures. +** Return: Number of pico-features returned (0 if an error occurred) +** Exceptions: none +** History: Tue Mar 12 17:55:18 1991, DSJ, Created. +*/ + FEATURE_SET Features; + int NumFeatures; + + NormMethod = baseline; + Features = ExtractPicoFeatures (Blob, LineStats); + + NumFeatures = NumFeaturesIn (Features); + if (NumFeatures > UNLIKELY_NUM_FEAT) { + FreeFeatureSet(Features); + return (0); + } + + ComputeIntFeatures(Features, IntFeatures); + *FloatFeatures = Features; + + return (NumFeatures); + +} /* GetAdaptiveFeatures */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int AdaptableWord(TWERD *Word, + const char *BestChoice, + const char *BestRawChoice) { +/* + ** Parameters: + ** Word + current word +** BestChoice + best overall choice for word with context +** BestRawChoice + best choice for word without context +** Globals: none +** Operation: Return TRUE if the specified word is acceptable for +** adaptation. +** Return: TRUE or FALSE +** Exceptions: none +** History: Thu May 30 14:25:06 1991, DSJ, Created. +*/ + int BestChoiceLength; + + return ( /* rules that apply in general - simplest to compute first */ + /* EnableLearning && */ + /* new rules */ + BestChoice != NULL && BestRawChoice != NULL && Word != NULL && (BestChoiceLength = strlen (BestChoice)) > 0 && BestChoiceLength == NumBlobsIn (Word) && BestChoiceLength <= MAX_ADAPTABLE_WERD_SIZE && ( + EnableNewAdaptRules + && + CurrentBestChoiceAdjustFactor + () + <= + ADAPTABLE_WERD + && + AlternativeChoicesWorseThan + (ADAPTABLE_WERD) + && + CurrentBestChoiceIs + (BestChoice) + || + /* old rules */ + !EnableNewAdaptRules + && + BestChoiceLength + == + strlen + (BestRawChoice) + && + ((valid_word (BestChoice) && case_ok (BestChoice)) || (valid_number (BestChoice) && pure_number (BestChoice))) && punctuation_ok (BestChoice) != -1 && punctuation_ok (BestChoice) <= 1)); + +} /* AdaptableWord */ + + +/*---------------------------------------------------------------------------*/ +void AdaptToChar(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold) { +/* + ** Parameters: + ** Blob + blob to add to templates for ClassId +** LineStats + statistics about text line blob is in +** ClassId + class to add blob to +** Threshold + minimum match rating to existing template +** Globals: +** AdaptedTemplates + current set of adapted templates +** AllProtosOn + dummy mask to match against all protos +** AllConfigsOn + dummy mask to match against all configs +** Operation: +** Return: none +** Exceptions: none +** History: Thu Mar 14 09:36:03 1991, DSJ, Created. +*/ + int NumFeatures; + INT_FEATURE_ARRAY IntFeatures; + INT_RESULT_STRUCT IntResult; + CLASS_INDEX ClassIndex; + INT_CLASS IClass; + ADAPT_CLASS Class; + TEMP_CONFIG TempConfig; + FEATURE_SET FloatFeatures; + + NumCharsAdaptedTo++; + if (!LegalClassId (ClassId)) + return; + + if (UnusedClassIdIn (AdaptedTemplates->Templates, ClassId)) { + MakeNewAdaptedClass(Blob, LineStats, ClassId, AdaptedTemplates); + } + else { + IClass = ClassForClassId (AdaptedTemplates->Templates, ClassId); + ClassIndex = IndexForClassId (AdaptedTemplates->Templates, ClassId); + Class = AdaptedTemplates->Class[ClassIndex]; + + NumFeatures = GetAdaptiveFeatures (Blob, LineStats, + IntFeatures, &FloatFeatures); + if (NumFeatures <= 0) + return; + + SetBaseLineMatch(); + IntegerMatcher (IClass, AllProtosOn, AllConfigsOn, + NumFeatures, NumFeatures, IntFeatures, 0, 0, + &IntResult, NO_DEBUG); + + SetAdaptiveThreshold(Threshold); + + if (IntResult.Rating <= Threshold) { + if (ConfigIsPermanent (Class, IntResult.Config)) { + if (LearningDebugLevel >= 1) + cprintf ("Found good match to perm config %d = %4.1f%%.\n", + IntResult.Config, (1.0 - IntResult.Rating) * 100.0); + FreeFeatureSet(FloatFeatures); + return; + } + + TempConfig = TempConfigFor (Class, IntResult.Config); + IncreaseConfidence(TempConfig); + if (LearningDebugLevel >= 1) + cprintf ("Increasing reliability of temp config %d to %d.\n", + IntResult.Config, TempConfig->NumTimesSeen); + + if (TempConfigReliable (TempConfig)) + MakePermanent (AdaptedTemplates, ClassId, IntResult.Config, + Blob, LineStats); + } + else { + if (LearningDebugLevel >= 1) + cprintf ("Found poor match to temp config %d = %4.1f%%.\n", + IntResult.Config, (1.0 - IntResult.Rating) * 100.0); + MakeNewTemporaryConfig(AdaptedTemplates, + ClassId, + NumFeatures, + IntFeatures, + FloatFeatures); + if (LearningDebugLevel >= 1) { + IntegerMatcher (IClass, AllProtosOn, AllConfigsOn, + NumFeatures, NumFeatures, IntFeatures, 0, 0, + &IntResult, NO_DEBUG); + cprintf ("Best match to temp config %d = %4.1f%%.\n", + IntResult.Config, (1.0 - IntResult.Rating) * 100.0); + if (LearningDebugLevel >= 2) { + UINT32 ConfigMask; + ConfigMask = 1 << IntResult.Config; + ShowMatchDisplay(); + IntegerMatcher (IClass, AllProtosOn, (BIT_VECTOR)&ConfigMask, + NumFeatures, NumFeatures, IntFeatures, 0, 0, + &IntResult, 6 | 0x19); + UpdateMatchDisplay(); + GetClassToDebug ("Adapting"); + } + } + } + FreeFeatureSet(FloatFeatures); + } +} /* AdaptToChar */ + + +/*---------------------------------------------------------------------------*/ +void AdaptToPunc(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold) { +/* + ** Parameters: + ** Blob + blob to add to templates for ClassId +** LineStats + statistics about text line blob is in +** ClassId + class to add blob to +** Threshold + minimum match rating to existing template +** Globals: +** PreTrainedTemplates + current set of built-in templates +** Operation: +** Return: none +** Exceptions: none +** History: Thu Mar 14 09:36:03 1991, DSJ, Created. +*/ + ADAPT_RESULTS Results; + int i; + + Results.BlobLength = MAX_FLOAT32; + Results.NumMatches = 0; + Results.BestRating = WORST_POSSIBLE_RATING; + Results.BestClass = NO_CLASS; + Results.BestConfig = 0; + InitMatcherRatings (Results.Ratings); + CharNormClassifier(Blob, LineStats, PreTrainedTemplates, &Results); + RemoveBadMatches(&Results); + + if (Results.NumMatches != 1) { + if (LearningDebugLevel >= 1) { + cprintf ("Rejecting punc = %c (Alternatives = ", ClassId); + + for (i = 0; i < Results.NumMatches; i++) + cprintf ("%c", Results.Classes[i]); + cprintf (")\n"); + } + return; + } + + #ifndef SECURE_NAMES + if (LearningDebugLevel >= 1) + cprintf ("Adapting to punc = %c\n", ClassId); + #endif + AdaptToChar(Blob, LineStats, ClassId, Threshold); + +} /* AdaptToPunc */ + + +/*---------------------------------------------------------------------------*/ +void AddNewResult(ADAPT_RESULTS *Results, + CLASS_ID ClassId, + FLOAT32 Rating, + int ConfigId) { +/* + ** Parameters: + ** Results + results to add new result to +** ClassId + class of new result +** Rating + rating of new result +** ConfigId + config id of new result +** Globals: +** BadMatchPad + defines limits of an acceptable match +** Operation: This routine adds the result of a classification into +** Results. If the new rating is much worse than the current +** best rating, it is not entered into results because it +** would end up being stripped later anyway. If the new rating +** is better than the old rating for the class, it replaces the +** old rating. If this is the first rating for the class, the +** class is added to the list of matched classes in Results. +** If the new rating is better than the best so far, it +** becomes the best so far. +** Return: none +** Exceptions: none +** History: Tue Mar 12 18:19:29 1991, DSJ, Created. +*/ + FLOAT32 OldRating; + INT_CLASS_STRUCT* CharClass = NULL; + + OldRating = Results->Ratings[ClassId]; + if (Rating <= Results->BestRating + BadMatchPad && Rating < OldRating) { + Results->Ratings[ClassId] = Rating; + if (ClassId != NO_CLASS) + CharClass = ClassForClassId(PreTrainedTemplates, ClassId); + if (CharClass != NULL && NumIntConfigsIn(CharClass) == 32) + Results->Configs[ClassId] = ConfigId; + else + Results->Configs[ClassId] = ~0; + + if (Rating < Results->BestRating) { + Results->BestRating = Rating; + Results->BestClass = ClassId; + Results->BestConfig = ConfigId; + } + + /* if this is first rating for class, add to list of classes matched */ + if (OldRating == WORST_POSSIBLE_RATING) + Results->Classes[Results->NumMatches++] = ClassId; + } +} /* AddNewResult */ + + +/*---------------------------------------------------------------------------*/ +void AmbigClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + char *Ambiguities, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob to be classified +** LineStats + statistics for text line Blob is in +** Templates + built-in templates to classify against +** Ambiguities + string of class id's to match against +** Results + place to put match results +** Globals: +** AllProtosOn + mask that enables all protos +** AllConfigsOn + mask that enables all configs +** Operation: This routine is identical to CharNormClassifier() +** except that it does no class pruning. It simply matches +** the unknown blob against the classes listed in +** Ambiguities. +** Return: none +** Exceptions: none +** History: Tue Mar 12 19:40:36 1991, DSJ, Created. +*/ + int IntOutlineLength; + int NumFeatures; + INT_FEATURE_ARRAY IntFeatures; + CLASS_NORMALIZATION_ARRAY CharNormArray; + INT_RESULT_STRUCT IntResult; + CLASS_ID ClassId; + CLASS_INDEX ClassIndex; + + AmbigClassifierCalls++; + + NumFeatures = GetCharNormFeatures (Blob, LineStats, + Templates, + IntFeatures, CharNormArray, + &(Results->BlobLength)); + if (NumFeatures <= 0) + return; + + IntOutlineLength = (int) (Results->BlobLength / GetPicoFeatureLength ()); + + if (MatcherDebugLevel >= 2) + cprintf ("AM Matches = "); + + while (*Ambiguities) { + ClassId = *Ambiguities; + ClassIndex = IndexForClassId (Templates, ClassId); + + SetCharNormMatch(); + IntegerMatcher (ClassForClassId (Templates, ClassId), + AllProtosOn, AllConfigsOn, + IntOutlineLength, NumFeatures, IntFeatures, 0, + CharNormArray[ClassIndex], &IntResult, NO_DEBUG); + + if (MatcherDebugLevel >= 2) + cprintf ("%c-%-2d %2.0f ", ClassId, IntResult.Config, + IntResult.Rating * 100.0); + + AddNewResult (Results, ClassId, IntResult.Rating, IntResult.Config); + + Ambiguities++; + + NumAmbigClassesTried++; + } + if (MatcherDebugLevel >= 2) + cprintf ("\n"); + +} /* AmbigClassifier */ + + +/*---------------------------------------------------------------------------*/ +char *BaselineClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_TEMPLATES Templates, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob to be classified +** LineStats + statistics for text line Blob is in +** Templates + current set of adapted templates +** Results + place to put match results +** Globals: +** BaselineCutoffs + expected num features for each class +** Operation: This routine extracts baseline normalized features +** from the unknown character and matches them against the +** specified set of templates. The classes which match +** are added to Results. +** Return: String of possible ambiguous chars that should be checked. +** Exceptions: none +** History: Tue Mar 12 19:38:03 1991, DSJ, Created. +*/ + int IntOutlineLength; + int NumFeatures; + int NumClasses; + int i; + int config; + float best_rating; + INT_FEATURE_ARRAY IntFeatures; + CLASS_NORMALIZATION_ARRAY CharNormArray; + CLASS_PRUNER_RESULTS ClassPrunerResults; + INT_RESULT_STRUCT IntResult; + CLASS_ID ClassId; + CLASS_INDEX ClassIndex; + ADAPT_CLASS Class; + + BaselineClassifierCalls++; + + NumFeatures = GetBaselineFeatures (Blob, LineStats, + Templates->Templates, + IntFeatures, CharNormArray, + &(Results->BlobLength)); + if (NumFeatures <= 0) + return NULL; + + IntOutlineLength = (int) (Results->BlobLength / GetPicoFeatureLength ()); + + NumClasses = ClassPruner (Templates->Templates, NumFeatures, + IntFeatures, CharNormArray, + BaselineCutoffs, ClassPrunerResults, + MatchDebugFlags); + + NumBaselineClassesTried += NumClasses; + + if (MatcherDebugLevel >= 2 || display_ratings > 1) + cprintf ("BL Matches = "); + + best_rating = WORST_POSSIBLE_RATING; + for (i = 0; i < NumClasses + && ((newcp_ratings_on & 12) < 8 + || (newcp_ratings_on & 12) == 8 + && ClassPrunerResults[i].Rating < best_rating + BadMatchPad / 2 + && ClassPrunerResults[i].Rating < newcp_duff_rating + && NumClasses > 1); i++) { + ClassId = ClassPrunerResults[i].Class; + ClassIndex = IndexForClassId (Templates->Templates, ClassId); + + SetBaseLineMatch(); + IntegerMatcher (ClassForClassId (Templates->Templates, ClassId), + Templates->Class[ClassIndex]->PermProtos, + Templates->Class[ClassIndex]->PermConfigs, + IntOutlineLength, NumFeatures, IntFeatures, 0, + CharNormArray[ClassIndex], &IntResult, MatchDebugFlags); + + if (MatcherDebugLevel >= 2 || display_ratings > 1) { + cprintf ("%c-%-2d %2.1f(%2.1f/%2.1f) ", ClassId, IntResult.Config, + IntResult.Rating * 100.0, + ClassPrunerResults[i].Rating * 100.0, + ClassPrunerResults[i].Rating2 * 100.0); + if (i % 4 == 3) + cprintf ("\n"); + } + + AddNewResult (Results, ClassId, IntResult.Rating, IntResult.Config); + } + while (i < NumClasses) { + ClassId = ClassPrunerResults[i].Class; + ClassIndex = IndexForClassId (Templates->Templates, ClassId); + Class = Templates->Class[ClassIndex]; + config = + NumIntConfigsIn (ClassForIndex (Templates->Templates, ClassIndex)); + for (config--; config >= 0 && !ConfigIsPermanent (Class, config); + config--); + + if (MatcherDebugLevel >= 2 || display_ratings > 1) { + cprintf ("%c(%d) %2.1f(%2.1f) ", ClassId, config, + ClassPrunerResults[i].Rating * 200.0, + ClassPrunerResults[i].Rating2 * 100.0); + if (i % 4 == 3) + cprintf ("\n"); + } + + AddNewResult (Results, ClassId, ClassPrunerResults[i].Rating * 2, + config); + i++; + } + if (MatcherDebugLevel >= 2 || display_ratings > 1) + cprintf ("\n"); + + ClassId = Results->BestClass; + if (ClassId == NO_CLASS) + return (NULL); + /* this is a bug - maybe should return "" */ + + ClassIndex = IndexForClassId (Templates->Templates, ClassId); + return ((char *) (Templates->Class[ClassIndex]-> + Config[Results->BestConfig].Perm)); + +} /* BaselineClassifier */ + + +/*---------------------------------------------------------------------------*/ +void make_config_pruner(INT_TEMPLATES templates, + CONFIG_PRUNER *config_pruner) { + int classid; + int x; //feature coord + int word_index; //in faster version + int bit_index; + UINT32 XFeatureAddress; + UINT32 YFeatureAddress; + UINT32 ThetaFeatureAddress; + INT_CLASS ClassTemplate; + int ProtoSetIndex; + PROTO_SET ProtoSet; + UINT32 *ProtoPrunerPtr; + UINT32 ProtoNum; + INT32 proto_offset; + UINT32 ConfigWord; + UINT32 ProtoWord; + INT_PROTO Proto; + UINT32 x_config_mask; //forming mask + UINT32 y_config_mask; //forming mask + UINT32 th_config_mask; //forming mask + + for (classid = 0; classid < NumClassesIn (templates); classid++) { + ClassTemplate = ClassForIndex (templates, classid); + for (x = 0; x < NUM_PP_BUCKETS; x++) { + XFeatureAddress = (x << 1); + YFeatureAddress = (NUM_PP_BUCKETS << 1) + (x << 1); + ThetaFeatureAddress = (NUM_PP_BUCKETS << 2) + (x << 1); + x_config_mask = 0; + y_config_mask = 0; + th_config_mask = 0; + for (ProtoSetIndex = 0; + ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ProtoPrunerPtr = (UINT32 *) ((*ProtoSet).ProtoPruner); + for (ProtoNum = 0; ProtoNum < PROTOS_PER_PROTO_SET; + ProtoNum += (PROTOS_PER_PROTO_SET >> 1), ProtoPrunerPtr++) { + /* Prune Protos of current Proto Set */ + ProtoWord = *(ProtoPrunerPtr + XFeatureAddress); + for (proto_offset = 0; ProtoWord != 0; + proto_offset++, ProtoWord >>= 1) { + if (ProtoWord & 1) { + Proto = + &(ProtoSet->Protos[ProtoNum + proto_offset]); + ConfigWord = Proto->Configs[0]; + x_config_mask |= ConfigWord; + } + } + + ProtoWord = *(ProtoPrunerPtr + YFeatureAddress); + for (proto_offset = 0; ProtoWord != 0; + proto_offset++, ProtoWord >>= 1) { + if (ProtoWord & 1) { + Proto = + &(ProtoSet->Protos[ProtoNum + proto_offset]); + ConfigWord = Proto->Configs[0]; + y_config_mask |= ConfigWord; + } + } + + ProtoWord = *(ProtoPrunerPtr + ThetaFeatureAddress); + for (proto_offset = 0; ProtoWord != 0; + proto_offset++, ProtoWord >>= 1) { + if (ProtoWord & 1) { + Proto = + &(ProtoSet->Protos[ProtoNum + proto_offset]); + ConfigWord = Proto->Configs[0]; + th_config_mask |= ConfigWord; + } + } + } + } + for (word_index = 0; word_index < 4; word_index++) { + ConfigWord = 0; + for (bit_index = 0; bit_index < 8; bit_index++) { + if (x_config_mask & 1) + ConfigWord |= 1 << (bit_index * 4); + x_config_mask >>= 1; + } + config_pruner[classid][0][x][word_index] = ConfigWord; + + ConfigWord = 0; + for (bit_index = 0; bit_index < 8; bit_index++) { + if (y_config_mask & 1) + ConfigWord |= 1 << (bit_index * 4); + y_config_mask >>= 1; + } + config_pruner[classid][1][x][word_index] = ConfigWord; + + ConfigWord = 0; + for (bit_index = 0; bit_index < 8; bit_index++) { + if (th_config_mask & 1) + ConfigWord |= 1 << (bit_index * 4); + th_config_mask >>= 1; + } + config_pruner[classid][2][x][word_index] = ConfigWord; + } + } + } +} + + +/*---------------------------------------------------------------------------*/ +void CharNormClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob to be classified +** LineStats + statistics for text line Blob is in +** Templates + templates to classify unknown against +** Results + place to put match results +** Globals: +** CharNormCutoffs + expected num features for each class +** AllProtosOn + mask that enables all protos +** AllConfigsOn + mask that enables all configs +** Operation: This routine extracts character normalized features +** from the unknown character and matches them against the +** specified set of templates. The classes which match +** are added to Results. +** Return: none +** Exceptions: none +** History: Tue Mar 12 16:02:52 1991, DSJ, Created. +*/ + int IntOutlineLength; + int NumFeatures; + int NumClasses; + int i; + INT32 min_misses; + float best_rating; + INT_FEATURE_ARRAY IntFeatures; + CLASS_NORMALIZATION_ARRAY CharNormArray; + CLASS_PRUNER_RESULTS ClassPrunerResults; + INT_RESULT_STRUCT IntResult; + CLASS_ID ClassId; + CLASS_INDEX ClassIndex; + + CharNormClassifierCalls++; + + NumFeatures = GetCharNormFeatures (Blob, LineStats, + Templates, + IntFeatures, CharNormArray, + &(Results->BlobLength)); + if (NumFeatures <= 0) + return; + + IntOutlineLength = (int) (Results->BlobLength / GetPicoFeatureLength ()); + + NumClasses = ClassPruner (Templates, NumFeatures, + IntFeatures, CharNormArray, + CharNormCutoffs, ClassPrunerResults, + MatchDebugFlags); + + if (feature_prune_percentile > 0) { + min_misses = feature_pruner (Templates, NumFeatures, + IntFeatures, NumClasses, + ClassPrunerResults); + NumClasses = + prune_configs(Templates, + min_misses, + NumFeatures, + IntFeatures, + CharNormArray, + NumClasses, + IntOutlineLength, + ClassPrunerResults, + MatchDebugFlags); + } + else + min_misses = 0; + if (tessedit_single_match && NumClasses > 1) + NumClasses = 1; + NumCharNormClassesTried += NumClasses; + + if (MatcherDebugLevel >= 2 || display_ratings > 1) + cprintf ("CN Matches = "); + + best_rating = WORST_POSSIBLE_RATING; + for (i = 0; i < NumClasses + && ((newcp_ratings_on & 3) < 2 + || (newcp_ratings_on & 3) == 2 + && ClassPrunerResults[i].Rating < best_rating + BadMatchPad / 2 + && ClassPrunerResults[i].Rating < newcp_duff_rating + && NumClasses > 1); i++) { + ClassId = ClassPrunerResults[i].Class; + ClassIndex = IndexForClassId (Templates, ClassId); + + SetCharNormMatch(); + + if (feature_prune_percentile > 0) + //xiaofan + config_mask_to_proto_mask (ClassForClassId (Templates, ClassId), (BIT_VECTOR) & ClassPrunerResults[i].config_mask, + PrunedProtos); + //xiaofan + IntegerMatcher (ClassForClassId (Templates, ClassId), PrunedProtos, (BIT_VECTOR) & ClassPrunerResults[i].config_mask, + IntOutlineLength, NumFeatures, IntFeatures, 0, + CharNormArray[ClassIndex], &IntResult, MatchDebugFlags); + + if (MatcherDebugLevel >= 2 || display_ratings > 1) { + cprintf ("%c-%-2d %2.1f(%2.1f/%2.1f) ", ClassId, IntResult.Config, + IntResult.Rating * 100.0, + ClassPrunerResults[i].Rating * 100.0, + ClassPrunerResults[i].Rating2 * 100.0); + if (i % 4 == 3) + cprintf ("\n"); + } + + AddNewResult (Results, ClassId, IntResult.Rating, IntResult.Config); + if (IntResult.Rating < best_rating) + best_rating = IntResult.Rating; + } + while (i < NumClasses) { + ClassId = ClassPrunerResults[i].Class; + ClassIndex = IndexForClassId (Templates, ClassId); + + if (MatcherDebugLevel >= 2 || display_ratings > 1) { + cprintf ("%c %2.1f(%2.1f) ", ClassId, + ClassPrunerResults[i].Rating * 200.0, + ClassPrunerResults[i].Rating2 * 100.0); + if (i % 4 == 3) + cprintf ("\n"); + } + + AddNewResult (Results, ClassId, ClassPrunerResults[i].Rating * 2, 0); + i++; + } + if (MatcherDebugLevel >= 2 || display_ratings > 1) + cprintf ("\n"); + +} /* CharNormClassifier */ + + +/*---------------------------------------------------------------------------*/ +void ClassifyAsNoise(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob to be classified +** LineStats + statistics for text line Blob is in +** Results + results to add noise classification to +** Globals: +** NoiseBlobLength + avg. length of a noise blob +** Operation: This routine computes a rating which reflects the +** likelihood that the blob being classified is a noise +** blob. NOTE: assumes that the blob length has already been +** computed and placed into Results. +** Return: none +** Exceptions: none +** History: Tue Mar 12 18:36:52 1991, DSJ, Created. +*/ + register FLOAT32 Rating; + + Rating = Results->BlobLength / NoiseBlobLength; + Rating *= Rating; + Rating /= 1.0 + Rating; + + AddNewResult (Results, NO_CLASS, Rating, 0); + +} /* ClassifyAsNoise */ + + +/*---------------------------------------------------------------------------*/ +int CompareCurrentRatings( //CLASS_ID *Class1, + const void *arg1, + const void *arg2) { //CLASS_ID *Class2) +/* + ** Parameters: + ** Class1, Class2 + classes whose ratings are to be compared +** Globals: +** CurrentRatings + contains actual ratings for each class +** Operation: This routine gets the ratings for the 2 specified classes +** from a global variable (CurrentRatings) and returns: +** -1 if Rating1 < Rating2 +** 0 if Rating1 = Rating2 +** 1 if Rating1 > Rating2 +** Return: Order of classes based on their ratings (see above). +** Exceptions: none +** History: Tue Mar 12 14:18:31 1991, DSJ, Created. +*/ + FLOAT32 Rating1, Rating2; + CLASS_ID *Class1 = (CLASS_ID *) arg1; + CLASS_ID *Class2 = (CLASS_ID *) arg2; + + Rating1 = CurrentRatings[*Class1]; + Rating2 = CurrentRatings[*Class2]; + + if (Rating1 < Rating2) + return (-1); + else if (Rating1 > Rating2) + return (1); + else + return (0); + +} /* CompareCurrentRatings */ + + +/*---------------------------------------------------------------------------*/ +LIST ConvertMatchesToChoices(ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Results + adaptive matcher results to convert to choices +** Globals: none +** Operation: This routine creates a choice for each matching class +** in Results (up to MAX_MATCHES) and returns a list of +** these choices. The match +** ratings are converted to be the ratings and certainties +** as used by the context checkers. +** Return: List of choices. +** Exceptions: none +** History: Tue Mar 12 08:55:37 1991, DSJ, Created. +*/ + char ChoiceString[2]; + int i; + LIST Choices; + CLASS_ID NextMatch; + FLOAT32 Rating; + FLOAT32 Certainty; + + ChoiceString[1] = '\0'; + if (Results->NumMatches > MAX_MATCHES) + Results->NumMatches = MAX_MATCHES; + + for (Choices = NIL, i = 0; i < Results->NumMatches; i++) { + NextMatch = Results->Classes[i]; + ChoiceString[0] = NextMatch; + Rating = Certainty = Results->Ratings[NextMatch]; + Rating *= RatingScale * Results->BlobLength; + Certainty *= -CertaintyScale; + Choices = append_choice (Choices, ChoiceString, Rating, Certainty, + Results->Configs[NextMatch]); + } + return (Choices); + +} /* ConvertMatchesToChoices */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void DebugAdaptiveClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob whose classification is being debugged +** LineStats + statistics for text line blob is in +** Results + results of match being debugged +** Globals: none +** Operation: +** Return: none +** Exceptions: none +** History: Wed Mar 13 16:44:41 1991, DSJ, Created. +*/ + const char *Prompt = + "\nType class id (or CTRL-A,B,C) in IntegerMatch Window ..."; + const char *DebugMode = "All Templates"; + CLASS_ID LastClass = Results->BestClass; + CLASS_ID ClassId; + BOOL8 AdaptiveOn = TRUE; + BOOL8 PreTrainedOn = TRUE; + + ShowMatchDisplay(); + cprintf ("\nDebugging class = %c (%s) ...\n", LastClass, DebugMode); + ShowBestMatchFor(Blob, LineStats, LastClass, AdaptiveOn, PreTrainedOn); + UpdateMatchDisplay(); + + while ((ClassId = GetClassToDebug (Prompt)) != 0) { + switch (ClassId) { + case 'b': + AdaptiveOn = TRUE; + PreTrainedOn = FALSE; + DebugMode = "Adaptive Templates Only"; + break; + + case 'c': + AdaptiveOn = FALSE; + PreTrainedOn = TRUE; + DebugMode = "PreTrained Templates Only"; + break; + + case 'a': + AdaptiveOn = TRUE; + PreTrainedOn = TRUE; + DebugMode = "All Templates"; + break; + + default: + LastClass = ClassId; + break; + } + + ShowMatchDisplay(); + cprintf ("\nDebugging class = %c (%s) ...\n", LastClass, DebugMode); + ShowBestMatchFor(Blob, LineStats, LastClass, AdaptiveOn, PreTrainedOn); + UpdateMatchDisplay(); + } +} /* DebugAdaptiveClassifier */ +#endif + +/*---------------------------------------------------------------------------*/ +void DoAdaptiveMatch(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob to be classified +** LineStats + statistics for text line Blob is in +** Results + place to put match results +** Globals: +** PreTrainedTemplates + built-in training templates +** AdaptedTemplates + templates adapted for this page +** GreatAdaptiveMatch + rating limit for a great match +** Operation: This routine performs an adaptive classification. +** If we have not yet adapted to enough classes, a simple +** classification to the pre-trained templates is performed. +** Otherwise, we match the blob against the adapted templates. +** If the adapted templates do not match well, we try a +** match against the pre-trained templates. If an adapted +** template match is found, we do a match to any pre-trained +** templates which could be ambiguous. The results from all +** of these classifications are merged together into Results. +** Return: none +** Exceptions: none +** History: Tue Mar 12 08:50:11 1991, DSJ, Created. +*/ + char *Ambiguities; + + AdaptiveMatcherCalls++; + InitIntFX(); + + if (AdaptedTemplates->NumPermClasses < MinNumPermClasses + || tess_cn_matching) { + CharNormClassifier(Blob, LineStats, PreTrainedTemplates, Results); + } + else { + Ambiguities = BaselineClassifier (Blob, LineStats, + AdaptedTemplates, Results); + + if (Results->NumMatches > 0 && MarginalMatch (Results->BestRating) + && !tess_bn_matching) { + CharNormClassifier(Blob, LineStats, PreTrainedTemplates, Results); + } + else if (Ambiguities && *Ambiguities) { + AmbigClassifier(Blob, + LineStats, + PreTrainedTemplates, + Ambiguities, + Results); + } + } + + if (Results->NumMatches == 0) + ClassifyAsNoise(Blob, LineStats, Results); + /**/} /* DoAdaptiveMatch */ + + /*---------------------------------------------------------------------------*/ + void + GetAdaptThresholds (TWERD * Word, + LINE_STATS * LineStats, + const char *BestChoice, + const char *BestRawChoice, FLOAT32 Thresholds[]) { + /* + ** Parameters: + ** Word + current word + ** LineStats + line stats for row word is in + ** BestChoice + best choice for current word with context + ** BestRawChoice + best choice for current word without context + ** Thresholds + array of thresholds to be filled in + ** Globals: + ** EnableNewAdaptRules + ** GoodAdaptiveMatch + ** PerfectRating + ** RatingMargin + ** Operation: This routine tries to estimate how tight the adaptation + ** threshold should be set for each character in the current + ** word. In general, the routine tries to set tighter + ** thresholds for a character when the current set of templates + ** would have made an error on that character. It tries + ** to set a threshold tight enough to eliminate the error. + ** Two different sets of rules can be used to determine the + ** desired thresholds. + ** Return: none (results are returned in Thresholds) + ** Exceptions: none + ** History: Fri May 31 09:22:08 1991, DSJ, Created. + */ + TBLOB *Blob; + + if (EnableNewAdaptRules) { /* new rules */ + FindClassifierErrors(PerfectRating, + GoodAdaptiveMatch, + RatingMargin, + Thresholds); + } + else { /* old rules */ + for (Blob = Word->blobs; + Blob != NULL; + Blob = Blob->next, BestChoice++, BestRawChoice++, Thresholds++) + if (*BestChoice == *BestRawChoice) + *Thresholds = GoodAdaptiveMatch; + else { + /* the blob was incorrectly classified - find the rating threshold + needed to create a template which will correct the error with + some margin. However, don't waste time trying to make + templates which are too tight. */ + *Thresholds = GetBestRatingFor (Blob, LineStats, *BestChoice); + *Thresholds *= (1.0 - RatingMargin); + if (*Thresholds > GoodAdaptiveMatch) + *Thresholds = GoodAdaptiveMatch; + if (*Thresholds < PerfectRating) + *Thresholds = PerfectRating; + } + } + } /* GetAdaptThresholds */ + + /*---------------------------------------------------------------------------*/ + char *GetAmbiguities(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID CorrectClass) { + /* + ** Parameters: + ** Blob + blob to get classification ambiguities for + ** LineStats + statistics for text line blob is in + ** CorrectClass + correct class for Blob + ** Globals: + ** CurrentRatings + used by qsort compare routine + ** PreTrainedTemplates + built-in templates + ** Operation: This routine matches blob to the built-in templates + ** to find out if there are any classes other than the correct + ** class which are potential ambiguities. + ** Return: String containing all possible ambiguous classes. + ** Exceptions: none + ** History: Fri Mar 15 08:08:22 1991, DSJ, Created. + */ + ADAPT_RESULTS Results; + char *Ambiguities; + int i; + + EnterClassifyMode; + + Results.NumMatches = 0; + Results.BestRating = WORST_POSSIBLE_RATING; + Results.BestClass = NO_CLASS; + Results.BestConfig = 0; + InitMatcherRatings (Results.Ratings); + + CharNormClassifier(Blob, LineStats, PreTrainedTemplates, &Results); + RemoveBadMatches(&Results); + + /* save ratings in a global so that CompareCurrentRatings() can see them */ + CurrentRatings = Results.Ratings; + qsort ((void *) (Results.Classes), Results.NumMatches, + sizeof (CLASS_ID), CompareCurrentRatings); + + /* copy the class id's into an string of ambiguities - don't copy if + the correct class is the only class id matched */ + Ambiguities = (char *) Emalloc (sizeof (char) * (Results.NumMatches + 1)); + if (Results.NumMatches > 1 || + Results.NumMatches == 1 && Results.Classes[0] != CorrectClass) { + for (i = 0; i < Results.NumMatches; i++) + Ambiguities[i] = Results.Classes[i]; + Ambiguities[i] = '\0'; + } + else + Ambiguities[0] = '\0'; + + return (Ambiguities); + + } /* GetAmbiguities */ + + /*---------------------------------------------------------------------------*/ + int GetBaselineFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength) { + /* + ** Parameters: + ** Blob + blob to extract features from + ** LineStats + statistics about text row blob is in + ** Templates + used to compute char norm adjustments + ** IntFeatures + array to fill with integer features + ** CharNormArray + array to fill with dummy char norm adjustments + ** BlobLength + length of blob in baseline-normalized units + ** Globals: none + ** Operation: This routine sets up the feature extractor to extract + ** baseline normalized pico-features. + ** The extracted pico-features are converted + ** to integer form and placed in IntFeatures. CharNormArray + ** is filled with 0's to indicate to the matcher that no + ** character normalization adjustment needs to be done. + ** The total length of all blob outlines + ** in baseline normalized units is also returned. + ** Return: Number of pico-features returned (0 if an error occurred) + ** Exceptions: none + ** History: Tue Mar 12 17:55:18 1991, DSJ, Created. + */ + FEATURE_SET Features; + int NumFeatures; + + if (EnableIntFX) + return (GetIntBaselineFeatures (Blob, LineStats, Templates, + IntFeatures, CharNormArray, BlobLength)); + + NormMethod = baseline; + Features = ExtractPicoFeatures (Blob, LineStats); + + NumFeatures = NumFeaturesIn (Features); + *BlobLength = NumFeatures * GetPicoFeatureLength (); + if (NumFeatures > UNLIKELY_NUM_FEAT) { + FreeFeatureSet(Features); + return (0); + } + + ComputeIntFeatures(Features, IntFeatures); + ClearCharNormArray(Templates, CharNormArray); + + FreeFeatureSet(Features); + return (NumFeatures); + + } /* GetBaselineFeatures */ + + /*---------------------------------------------------------------------------*/ + FLOAT32 GetBestRatingFor(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId) { + /* + ** Parameters: + ** Blob + blob to get best rating for + ** LineStats + statistics about text line blob is in + ** ClassId + class blob is to be compared to + ** Globals: + ** PreTrainedTemplates + built-in templates + ** AdaptedTemplates + current set of adapted templates + ** AllProtosOn + dummy mask to enable all protos + ** AllConfigsOn + dummy mask to enable all configs + ** Operation: This routine classifies Blob against both sets of + ** templates for the specified class and returns the best + ** rating found. + ** Return: Best rating for match of Blob to ClassId. + ** Exceptions: none + ** History: Tue Apr 9 09:01:24 1991, DSJ, Created. + */ + int CNOutlineLength, BLOutlineLength; + int NumCNFeatures, NumBLFeatures; + INT_FEATURE_ARRAY CNFeatures, BLFeatures; + INT_RESULT_STRUCT CNResult, BLResult; + CLASS_NORMALIZATION_ARRAY CNAdjust, BLAdjust; + CLASS_INDEX ClassIndex; + FLOAT32 BlobLength; + + CNResult.Rating = BLResult.Rating = 1.0; + + if (!LegalClassId (ClassId)) + return (1.0); + + if (!UnusedClassIdIn (PreTrainedTemplates, ClassId)) { + NumCNFeatures = GetCharNormFeatures (Blob, LineStats, + PreTrainedTemplates, + CNFeatures, CNAdjust, &BlobLength); + if (NumCNFeatures > 0) { + CNOutlineLength = (int) (BlobLength / GetPicoFeatureLength ()); + ClassIndex = IndexForClassId (PreTrainedTemplates, ClassId); + + SetCharNormMatch(); + IntegerMatcher (ClassForClassId (PreTrainedTemplates, ClassId), + AllProtosOn, AllConfigsOn, + CNOutlineLength, NumCNFeatures, CNFeatures, 0, + CNAdjust[ClassIndex], &CNResult, NO_DEBUG); + } + } + + if (!UnusedClassIdIn (AdaptedTemplates->Templates, ClassId)) { + NumBLFeatures = GetBaselineFeatures (Blob, LineStats, + AdaptedTemplates->Templates, + BLFeatures, BLAdjust, &BlobLength); + if (NumBLFeatures > 0) { + BLOutlineLength = (int) (BlobLength / GetPicoFeatureLength ()); + ClassIndex = IndexForClassId (AdaptedTemplates->Templates, ClassId); + + SetBaseLineMatch(); + IntegerMatcher (ClassForClassId + (AdaptedTemplates->Templates, ClassId), + AdaptedTemplates->Class[ClassIndex]->PermProtos, + AdaptedTemplates->Class[ClassIndex]->PermConfigs, + BLOutlineLength, NumBLFeatures, BLFeatures, 0, + BLAdjust[ClassIndex], &BLResult, NO_DEBUG); + } + } + + return (MIN (BLResult.Rating, CNResult.Rating)); + + } /* GetBestRatingFor */ + + /*---------------------------------------------------------------------------*/ + int GetCharNormFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength) { + /* + ** Parameters: + ** Blob + blob to extract features from + ** LineStats + statistics about text row blob is in + ** Templates + used to compute char norm adjustments + ** IntFeatures + array to fill with integer features + ** CharNormArray + array to fill with char norm adjustments + ** BlobLength + length of blob in baseline-normalized units + ** Globals: none + ** Operation: This routine sets up the feature extractor to extract + ** character normalization features and character normalized + ** pico-features. The extracted pico-features are converted + ** to integer form and placed in IntFeatures. The character + ** normalization features are matched to each class in + ** templates and the resulting adjustment factors are returned + ** in CharNormArray. The total length of all blob outlines + ** in baseline normalized units is also returned. + ** Return: Number of pico-features returned (0 if an error occurred) + ** Exceptions: none + ** History: Tue Mar 12 17:55:18 1991, DSJ, Created. + */ + return (GetIntCharNormFeatures (Blob, LineStats, Templates, + IntFeatures, CharNormArray, BlobLength)); + } /* GetCharNormFeatures */ + + /*---------------------------------------------------------------------------*/ + int GetIntBaselineFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength) { + /* + ** Parameters: + ** Blob + blob to extract features from + ** LineStats + statistics about text row blob is in + ** Templates + used to compute char norm adjustments + ** IntFeatures + array to fill with integer features + ** CharNormArray + array to fill with dummy char norm adjustments + ** BlobLength + length of blob in baseline-normalized units + ** Globals: + ** FeaturesHaveBeenExtracted + TRUE if fx has been done + ** BaselineFeatures + holds extracted baseline feat + ** CharNormFeatures + holds extracted char norm feat + ** FXInfo + holds misc. FX info + ** Operation: This routine calls the integer (Hardware) feature + ** extractor if it has not been called before for this blob. + ** The results from the feature extractor are placed into + ** globals so that they can be used in other routines without + ** re-extracting the features. + ** It then copies the baseline features into the IntFeatures + ** array provided by the caller. + ** Return: Number of features extracted or 0 if an error occured. + ** Exceptions: none + ** History: Tue May 28 10:40:52 1991, DSJ, Created. + */ + register INT_FEATURE Src, Dest, End; + + if (!FeaturesHaveBeenExtracted) { + FeaturesOK = ExtractIntFeat (Blob, BaselineFeatures, + CharNormFeatures, &FXInfo); + FeaturesHaveBeenExtracted = TRUE; + } + + if (!FeaturesOK) { + *BlobLength = FXInfo.Length * ComputeScaleFactor (LineStats); + return (0); + } + + for (Src = BaselineFeatures, End = Src + FXInfo.NumBL, Dest = IntFeatures; + Src < End; *Dest++ = *Src++); + + ClearCharNormArray(Templates, CharNormArray); + *BlobLength = FXInfo.Length * ComputeScaleFactor (LineStats); + return (FXInfo.NumBL); + + } /* GetIntBaselineFeatures */ + + /*---------------------------------------------------------------------------*/ + int GetIntCharNormFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength) { + /* + ** Parameters: + ** Blob + blob to extract features from + ** LineStats + statistics about text row blob is in + ** Templates + used to compute char norm adjustments + ** IntFeatures + array to fill with integer features + ** CharNormArray + array to fill with dummy char norm adjustments + ** BlobLength + length of blob in baseline-normalized units + ** Globals: + ** FeaturesHaveBeenExtracted + TRUE if fx has been done + ** BaselineFeatures + holds extracted baseline feat + ** CharNormFeatures + holds extracted char norm feat + ** FXInfo + holds misc. FX info + ** Operation: This routine calls the integer (Hardware) feature + ** extractor if it has not been called before for this blob. + ** The results from the feature extractor are placed into + ** globals so that they can be used in other routines without + ** re-extracting the features. + ** It then copies the char norm features into the IntFeatures + ** array provided by the caller. + ** Return: Number of features extracted or 0 if an error occured. + ** Exceptions: none + ** History: Tue May 28 10:40:52 1991, DSJ, Created. + */ + register INT_FEATURE Src, Dest, End; + FEATURE NormFeature; + FLOAT32 Baseline, Scale; + + if (!FeaturesHaveBeenExtracted) { + FeaturesOK = ExtractIntFeat (Blob, BaselineFeatures, + CharNormFeatures, &FXInfo); + FeaturesHaveBeenExtracted = TRUE; + } + + if (!FeaturesOK) { + *BlobLength = FXInfo.Length * ComputeScaleFactor (LineStats); + return (0); + } + + for (Src = CharNormFeatures, End = Src + FXInfo.NumCN, Dest = IntFeatures; + Src < End; *Dest++ = *Src++); + + NormFeature = NewFeature (&CharNormDesc); + Baseline = BaselineAt (LineStats, FXInfo.Xmean); + Scale = ComputeScaleFactor (LineStats); + ParamOf (NormFeature, CharNormY) = (FXInfo.Ymean - Baseline) * Scale; + ParamOf (NormFeature, CharNormLength) = + FXInfo.Length * Scale / LENGTH_COMPRESSION; + ParamOf (NormFeature, CharNormRx) = FXInfo.Rx * Scale; + ParamOf (NormFeature, CharNormRy) = FXInfo.Ry * Scale; + ComputeIntCharNormArray(NormFeature, Templates, CharNormArray); + FreeFeature(NormFeature); + + *BlobLength = FXInfo.Length * Scale; + return (FXInfo.NumCN); + + } /* GetIntCharNormFeatures */ + + /*---------------------------------------------------------------------------*/ + void InitMatcherRatings(register FLOAT32 *Rating) { + /* + ** Parameters: + ** Rating + ptr to array of ratings to be initialized + ** Globals: none + ** Operation: This routine initializes the best rating for each class + ** to be the worst possible rating (1.0). + ** Return: none + ** Exceptions: none + ** History: Tue Mar 12 13:43:28 1991, DSJ, Created. + */ + register FLOAT32 *LastRating; + register FLOAT32 WorstRating = WORST_POSSIBLE_RATING; + + for (LastRating = Rating + MAX_CLASS_ID; + Rating <= LastRating; *Rating++ = WorstRating); + + } /* InitMatcherRatings */ + + /*---------------------------------------------------------------------------*/ + void MakeNewTemporaryConfig(ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int NumFeatures, + INT_FEATURE_ARRAY Features, + FEATURE_SET FloatFeatures) { + /* + ** Parameters: + ** Templates + adapted templates to add new config to + ** ClassId + class id to associate with new config + ** NumFeatures + number of features in IntFeatures + ** Features + features describing model for new config + ** FloatFeatures + floating-pt representation of features + ** Globals: + ** AllProtosOn + mask to enable all protos + ** AllConfigsOff + mask to disable all configs + ** TempProtoMask + defines old protos matched in new config + ** Operation: + ** Return: none + ** Exceptions: none + ** History: Fri Mar 15 08:49:46 1991, DSJ, Created. + */ + CLASS_INDEX ClassIndex; + INT_CLASS IClass; + ADAPT_CLASS Class; + PROTO_ID OldProtos[MAX_NUM_PROTOS]; + FEATURE_ID BadFeatures[MAX_NUM_INT_FEATURES]; + int NumOldProtos; + int NumBadFeatures; + int MaxProtoId, OldMaxProtoId; + int BlobLength = 0; + int MaskSize; + int ConfigId; + TEMP_CONFIG Config; + int i; + int debug_level = NO_DEBUG; + + if (LearningDebugLevel >= 3) + debug_level = + PRINT_MATCH_SUMMARY | PRINT_FEATURE_MATCHES | PRINT_PROTO_MATCHES; + + ClassIndex = IndexForClassId (Templates->Templates, ClassId); + IClass = ClassForClassId (Templates->Templates, ClassId); + Class = Templates->Class[ClassIndex]; + + if (NumIntConfigsIn (IClass) >= MAX_NUM_CONFIGS) + return; + + OldMaxProtoId = NumIntProtosIn (IClass) - 1; + + NumOldProtos = FindGoodProtos (IClass, AllProtosOn, AllConfigsOff, + BlobLength, NumFeatures, Features, + OldProtos, debug_level); + NumOldProtos = 0; + + MaskSize = WordsInVectorOfSize (MAX_NUM_PROTOS); + zero_all_bits(TempProtoMask, MaskSize); + for (i = 0; i < NumOldProtos; i++) + SET_BIT (TempProtoMask, OldProtos[i]); + + NumBadFeatures = FindBadFeatures (IClass, TempProtoMask, AllConfigsOn, + BlobLength, NumFeatures, Features, + BadFeatures, debug_level); + + MaxProtoId = MakeNewTempProtos (FloatFeatures, NumBadFeatures, BadFeatures, + IClass, Class, TempProtoMask); + if (MaxProtoId == NO_PROTO) + return; + + ConfigId = AddIntConfig (IClass); + ConvertConfig(TempProtoMask, ConfigId, IClass); + Config = NewTempConfig (MaxProtoId); + TempConfigFor (Class, ConfigId) = Config; + copy_all_bits (TempProtoMask, Config->Protos, Config->ProtoVectorSize); + + if (LearningDebugLevel >= 1) + cprintf ("Making new temp config %d using %d old and %d new protos.\n", + ConfigId, NumOldProtos, MaxProtoId - OldMaxProtoId); + + } /* MakeNewTemporaryConfig */ + + /*---------------------------------------------------------------------------*/ + PROTO_ID + MakeNewTempProtos (FEATURE_SET Features, + int NumBadFeat, + FEATURE_ID BadFeat[], + INT_CLASS IClass, + ADAPT_CLASS Class, BIT_VECTOR TempProtoMask) { + /* + ** Parameters: + ** Features + floating-pt features describing new character + ** NumBadFeat + number of bad features to turn into protos + ** BadFeat + feature id's of bad features + ** IClass + integer class templates to add new protos to + ** Class + adapted class templates to add new protos to + ** TempProtoMask + proto mask to add new protos to + ** Globals: none + ** Operation: This routine finds sets of sequential bad features + ** that all have the same angle and converts each set into + ** a new temporary proto. The temp proto is added to the + ** proto pruner for IClass, pushed onto the list of temp + ** protos in Class, and added to TempProtoMask. + ** Return: Max proto id in class after all protos have been added. + ** Exceptions: none + ** History: Fri Mar 15 11:39:38 1991, DSJ, Created. + */ + FEATURE_ID *ProtoStart; + FEATURE_ID *ProtoEnd; + FEATURE_ID *LastBad; + TEMP_PROTO TempProto; + PROTO Proto; + FEATURE F1, F2; + FLOAT32 X1, X2, Y1, Y2; + FLOAT32 A1, A2, AngleDelta; + FLOAT32 SegmentLength; + PROTO_ID Pid; + + for (ProtoStart = BadFeat, LastBad = ProtoStart + NumBadFeat; + ProtoStart < LastBad; ProtoStart = ProtoEnd) { + F1 = FeatureIn (Features, *ProtoStart); + X1 = ParamOf (F1, PicoFeatX); + Y1 = ParamOf (F1, PicoFeatY); + A1 = ParamOf (F1, PicoFeatDir); + + for (ProtoEnd = ProtoStart + 1, + SegmentLength = GetPicoFeatureLength (); + ProtoEnd < LastBad; + ProtoEnd++, SegmentLength += GetPicoFeatureLength ()) { + F2 = FeatureIn (Features, *ProtoEnd); + X2 = ParamOf (F2, PicoFeatX); + Y2 = ParamOf (F2, PicoFeatY); + A2 = ParamOf (F2, PicoFeatDir); + + AngleDelta = fabs (A1 - A2); + if (AngleDelta > 0.5) + AngleDelta = 1.0 - AngleDelta; + + if (AngleDelta > MaxAngleDelta || + fabs (X1 - X2) > SegmentLength || + fabs (Y1 - Y2) > SegmentLength) + break; + } + + F2 = FeatureIn (Features, *(ProtoEnd - 1)); + X2 = ParamOf (F2, PicoFeatX); + Y2 = ParamOf (F2, PicoFeatY); + A2 = ParamOf (F2, PicoFeatDir); + + Pid = AddIntProto (IClass); + if (Pid == NO_PROTO) + return (NO_PROTO); + + TempProto = NewTempProto (); + Proto = &(TempProto->Proto); + + /* compute proto params - NOTE that Y_DIM_OFFSET must be used because + ConvertProto assumes that the Y dimension varies from -0.5 to 0.5 + instead of the -0.25 to 0.75 used in baseline normalization */ + ProtoLength (Proto) = SegmentLength; + ProtoAngle (Proto) = A1; + ProtoX (Proto) = (X1 + X2) / 2.0; + ProtoY (Proto) = (Y1 + Y2) / 2.0 - Y_DIM_OFFSET; + FillABC(Proto); + + TempProto->ProtoId = Pid; + SET_BIT(TempProtoMask, Pid); + + ConvertProto(Proto, Pid, IClass); + AddProtoToProtoPruner(Proto, Pid, IClass); + + Class->TempProtos = push (Class->TempProtos, TempProto); + } + return (NumIntProtosIn (IClass) - 1); + } /* MakeNewTempProtos */ + + /*---------------------------------------------------------------------------*/ + void MakePermanent(ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int ConfigId, + TBLOB *Blob, + LINE_STATS *LineStats) { + /* + ** Parameters: + ** Templates + current set of adaptive templates + ** ClassId + class containing config to be made permanent + ** ConfigId + config to be made permanent + ** Blob + current blob being adapted to + ** LineStats + statistics about text line Blob is in + ** Globals: none + ** Operation: + ** Return: none + ** Exceptions: none + ** History: Thu Mar 14 15:54:08 1991, DSJ, Created. + */ + char *Ambigs; + TEMP_CONFIG Config; + CLASS_INDEX ClassIndex; + ADAPT_CLASS Class; + PROTO_KEY ProtoKey; + + ClassIndex = IndexForClassId (Templates->Templates, ClassId); + Class = Templates->Class[ClassIndex]; + Config = TempConfigFor (Class, ConfigId); + + MakeConfigPermanent(Class, ConfigId); + if (Class->NumPermConfigs == 0) + Templates->NumPermClasses++; + Class->NumPermConfigs++; + + ProtoKey.Templates = Templates; + ProtoKey.ClassId = ClassId; + ProtoKey.ConfigId = ConfigId; + Class->TempProtos = delete_d (Class->TempProtos, &ProtoKey, + MakeTempProtoPerm); + FreeTempConfig(Config); + + Ambigs = GetAmbiguities (Blob, LineStats, ClassId); + PermConfigFor (Class, ConfigId) = Ambigs; + + if (LearningDebugLevel >= 1) + cprintf ("Making config %d permanent with ambiguities '%s'.\n", + ConfigId, Ambigs); + + } /* MakePermanent */ + + /*---------------------------------------------------------------------------*/ + int MakeTempProtoPerm(void *item1, //TEMP_PROTO TempProto, + void *item2) { //PROTO_KEY *ProtoKey) + /* + ** Parameters: + ** TempProto + temporary proto to compare to key + ** ProtoKey + defines which protos to make permanent + ** Globals: none + ** Operation: This routine converts TempProto to be permanent if + ** its proto id is used by the configuration specified in + ** ProtoKey. + ** Return: TRUE if TempProto is converted, FALSE otherwise + ** Exceptions: none + ** History: Thu Mar 14 18:49:54 1991, DSJ, Created. + */ + CLASS_INDEX ClassIndex; + ADAPT_CLASS Class; + TEMP_CONFIG Config; + TEMP_PROTO TempProto; + PROTO_KEY *ProtoKey; + + TempProto = (TEMP_PROTO) item1; + ProtoKey = (PROTO_KEY *) item2; + + ClassIndex = IndexForClassId (ProtoKey->Templates->Templates, + ProtoKey->ClassId); + Class = ProtoKey->Templates->Class[ClassIndex]; + Config = TempConfigFor (Class, ProtoKey->ConfigId); + + if (TempProto->ProtoId > Config->MaxProtoId || + !test_bit (Config->Protos, TempProto->ProtoId)) + return (FALSE); + + MakeProtoPermanent (Class, TempProto->ProtoId); + AddProtoToClassPruner (&(TempProto->Proto), ProtoKey->ClassId, + ProtoKey->Templates->Templates); + FreeTempProto(TempProto); + + return (TRUE); + + } /* MakeTempProtoPerm */ + + /*---------------------------------------------------------------------------*/ + int NumBlobsIn(TWERD *Word) { + /* + ** Parameters: + ** Word + word to count blobs in + ** Globals: none + ** Operation: This routine returns the number of blobs in Word. + ** Return: Number of blobs in Word. + ** Exceptions: none + ** History: Thu Mar 14 08:30:27 1991, DSJ, Created. + */ + register TBLOB *Blob; + register int NumBlobs; + + if (Word == NULL) + return (0); + + for (Blob = Word->blobs, NumBlobs = 0; + Blob != NULL; Blob = Blob->next, NumBlobs++); + + return (NumBlobs); + + } /* NumBlobsIn */ + + /*---------------------------------------------------------------------------*/ + int NumOutlinesInBlob(TBLOB *Blob) { + /* + ** Parameters: + ** Blob + blob to count outlines in + ** Globals: none + ** Operation: This routine returns the number of OUTER outlines + ** in Blob. + ** Return: Number of outer outlines in Blob. + ** Exceptions: none + ** History: Mon Jun 10 15:46:20 1991, DSJ, Created. + */ + register TESSLINE *Outline; + register int NumOutlines; + + if (Blob == NULL) + return (0); + + for (Outline = Blob->outlines, NumOutlines = 0; + Outline != NULL; Outline = Outline->next, NumOutlines++); + + return (NumOutlines); + + } /* NumOutlinesInBlob */ + + /*---------------------------------------------------------------------------*/ + void PrintAdaptiveMatchResults(FILE *File, ADAPT_RESULTS *Results) { + /* + ** Parameters: + ** File + open text file to write Results to + ** Results + match results to write to File + ** Globals: none + ** Operation: This routine writes the matches in Results to File. + ** Return: none + ** Exceptions: none + ** History: Mon Mar 18 09:24:53 1991, DSJ, Created. + */ + int i; + + if (Results->NumMatches > 0) { + cprintf ("%c(%d) %4.1f ", Results->Classes[0], Results->Classes[0], + Results->Ratings[Results->Classes[0]] * 100.0); + + for (i = 1; i < Results->NumMatches; i++) { + cprintf ("%c(%d) %4.1f ", Results->Classes[i], + Results->Classes[i], + Results->Ratings[Results->Classes[i]] * 100.0); + } + } + } /* PrintAdaptiveMatchResults */ + + /*---------------------------------------------------------------------------*/ + void RemoveBadMatches(ADAPT_RESULTS *Results) { + /* + ** Parameters: + ** Results + contains matches to be filtered + ** Globals: + ** BadMatchPad + defines a "bad match" + ** Operation: This routine steps thru each matching class in Results + ** and removes it from the match list if its rating + ** is worse than the BestRating plus a pad. In other words, + ** all good matches get moved to the front of the classes + ** array. + ** Return: none + ** Exceptions: none + ** History: Tue Mar 12 13:51:03 1991, DSJ, Created. + */ + int Next, NextGood; + FLOAT32 *Rating = Results->Ratings; + CLASS_ID *Match = Results->Classes; + FLOAT32 BadMatchThreshold; + static const char* romans = "ivxIVX"; + BadMatchThreshold = Results->BestRating + BadMatchPad; + + if (bln_numericmode) { + for (Next = NextGood = 0; Next < Results->NumMatches; Next++) { + if (Rating[Match[Next]] <= BadMatchThreshold) { + if (!isalpha(Match[Next]) || strchr(romans, Match[Next]) != NULL) { + Match[NextGood++] = Match[Next]; + } else if (Match[Next] == 'l' && Rating['1'] >= BadMatchThreshold) { + Match[NextGood++] = '1'; + Rating['1'] = Rating['l']; + } else if (Match[Next] == 'O' && Rating['0'] >= BadMatchThreshold) { + Match[NextGood++] = '0'; + Rating['0'] = Rating['O']; + } + } + } + } + else { + for (Next = NextGood = 0; Next < Results->NumMatches; Next++) { + if (Rating[Match[Next]] <= BadMatchThreshold) + Match[NextGood++] = Match[Next]; + } + } + + Results->NumMatches = NextGood; + + } /* RemoveBadMatches */ + + /*----------------------------------------------------------------------------------*/ + void RemoveExtraPuncs(ADAPT_RESULTS *Results) { + /* + ** Parameters: + ** Results + contains matches to be filtered + ** Globals: + ** BadMatchPad + defines a "bad match" + ** Operation: This routine steps thru each matching class in Results + ** and removes it from the match list if its rating + ** is worse than the BestRating plus a pad. In other words, + ** all good matches get moved to the front of the classes + ** array. + ** Return: none + ** Exceptions: none + ** History: Tue Mar 12 13:51:03 1991, DSJ, Created. + */ + int Next, NextGood; + int punc_count; /*no of garbage characters */ + int digit_count; + CLASS_ID *Match = Results->Classes; + /*garbage characters */ + static char punc_chars[] = ".,;:/`~'-=\\|\"!_^"; + static char digit_chars[] = "0123456789"; + + punc_count = 0; + digit_count = 0; + for (Next = NextGood = 0; Next < Results->NumMatches; Next++) { + if (strchr (punc_chars, Match[Next]) == NULL) { + if (strchr (digit_chars, Match[Next]) == NULL) { + Match[NextGood++] = Match[Next]; + } + else { + if (digit_count < 1) + Match[NextGood++] = Match[Next]; + digit_count++; + } + } + else { + if (punc_count < 2) + Match[NextGood++] = Match[Next]; + punc_count++; /*count them */ + } + } + Results->NumMatches = NextGood; + } /* RemoveExtraPuncs */ + + /*---------------------------------------------------------------------------*/ + void SetAdaptiveThreshold(FLOAT32 Threshold) { + /* + ** Parameters: + ** Threshold + threshold for creating new templates + ** Globals: + ** GoodAdaptiveMatch + default good match rating + ** Operation: This routine resets the internal thresholds inside + ** the integer matcher to correspond to the specified + ** threshold. + ** Return: none + ** Exceptions: none + ** History: Tue Apr 9 08:33:13 1991, DSJ, Created. + */ + if (Threshold == GoodAdaptiveMatch) { + /* the blob was probably classified correctly - use the default rating + threshold */ + SetProtoThresh (0.9); + SetFeatureThresh (0.9); + } + else { + /* the blob was probably incorrectly classified */ + SetProtoThresh (1.0 - Threshold); + SetFeatureThresh (1.0 - Threshold); + } + } /* SetAdaptiveThreshold */ + + /*---------------------------------------------------------------------------*/ + void ShowBestMatchFor(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + BOOL8 AdaptiveOn, + BOOL8 PreTrainedOn) { + /* + ** Parameters: + ** Blob + blob to show best matching config for + ** LineStats + statistics for text line Blob is in + ** ClassId + class whose configs are to be searched + ** AdaptiveOn + TRUE if adaptive configs are enabled + ** PreTrainedOn + TRUE if pretrained configs are enabled + ** Globals: + ** PreTrainedTemplates + built-in training + ** AdaptedTemplates + adaptive templates + ** AllProtosOn + dummy proto mask + ** AllConfigsOn + dummy config mask + ** Operation: This routine compares Blob to both sets of templates + ** (adaptive and pre-trained) and then displays debug + ** information for the config which matched best. + ** Return: none + ** Exceptions: none + ** History: Fri Mar 22 08:43:52 1991, DSJ, Created. + */ + int CNOutlineLength = 0, BLOutlineLength = 0; + int NumCNFeatures = 0, NumBLFeatures = 0; + INT_FEATURE_ARRAY CNFeatures, BLFeatures; + INT_RESULT_STRUCT CNResult, BLResult; + CLASS_NORMALIZATION_ARRAY CNAdjust, BLAdjust; + CLASS_INDEX ClassIndex; + FLOAT32 BlobLength; + UINT32 ConfigMask; + static int next_config = -1; + + if (PreTrainedOn) next_config = -1; + + CNResult.Rating = BLResult.Rating = 2.0; + + if (!LegalClassId (ClassId)) { + cprintf ("%c is not a legal class!!\n", ClassId); + return; + } + + if (PreTrainedOn) + if (UnusedClassIdIn (PreTrainedTemplates, ClassId)) + cprintf ("No built-in templates for class '%c'\n", ClassId); + else { + NumCNFeatures = GetCharNormFeatures (Blob, LineStats, + PreTrainedTemplates, + CNFeatures, CNAdjust, + &BlobLength); + if (NumCNFeatures <= 0) + cprintf ("Illegal blob (char norm features)!\n"); + else { + CNOutlineLength = (int) (BlobLength / GetPicoFeatureLength ()); + ClassIndex = IndexForClassId (PreTrainedTemplates, ClassId); + + SetCharNormMatch(); + IntegerMatcher (ClassForClassId (PreTrainedTemplates, ClassId), + AllProtosOn, AllConfigsOn, + CNOutlineLength, NumCNFeatures, CNFeatures, 0, + CNAdjust[ClassIndex], &CNResult, NO_DEBUG); + + cprintf ("Best built-in template match is config %2d (%4.1f) (cn=%d)\n", + CNResult.Config, CNResult.Rating * 100.0, CNAdjust[ClassIndex]); + } + } + + if (AdaptiveOn) + if (UnusedClassIdIn (AdaptedTemplates->Templates, ClassId)) + cprintf ("No AD templates for class '%c'\n", ClassId); + else { + NumBLFeatures = GetBaselineFeatures (Blob, LineStats, + AdaptedTemplates->Templates, + BLFeatures, BLAdjust, + &BlobLength); + if (NumBLFeatures <= 0) + cprintf ("Illegal blob (baseline features)!\n"); + else { + BLOutlineLength = (int) (BlobLength / GetPicoFeatureLength ()); + ClassIndex = + IndexForClassId (AdaptedTemplates->Templates, ClassId); + + SetBaseLineMatch(); + IntegerMatcher (ClassForClassId + (AdaptedTemplates->Templates, ClassId), + AllProtosOn, AllConfigsOn, +// AdaptedTemplates->Class[ClassIndex]->PermProtos, +// AdaptedTemplates->Class[ClassIndex]->PermConfigs, + BLOutlineLength, NumBLFeatures, BLFeatures, 0, + BLAdjust[ClassIndex], &BLResult, NO_DEBUG); + + #ifndef SECURE_NAMES + cprintf ("Best adaptive template match is config %2d (%4.1f)\n", + BLResult.Config, BLResult.Rating * 100.0); + #endif + } + } + + cprintf ("\n"); + if (BLResult.Rating < CNResult.Rating) { + ClassIndex = IndexForClassId (AdaptedTemplates->Templates, ClassId); + if (next_config < 0) { + ConfigMask = 1 << BLResult.Config; + next_config = 0; + } else { + ConfigMask = 1 << next_config; + ++next_config; + } + NormMethod = baseline; + + SetBaseLineMatch(); + IntegerMatcher (ClassForClassId (AdaptedTemplates->Templates, ClassId), + AllProtosOn, +// AdaptedTemplates->Class[ClassIndex]->PermProtos, + (BIT_VECTOR) & ConfigMask, + BLOutlineLength, NumBLFeatures, BLFeatures, 0, + BLAdjust[ClassIndex], &BLResult, MatchDebugFlags); + cprintf ("Adaptive template match for config %2d is %4.1f\n", + BLResult.Config, BLResult.Rating * 100.0); + } + else { + ClassIndex = IndexForClassId (PreTrainedTemplates, ClassId); + ConfigMask = 1 << CNResult.Config; + NormMethod = character; + + SetCharNormMatch(); + //xiaofan + IntegerMatcher (ClassForClassId (PreTrainedTemplates, ClassId), AllProtosOn, (BIT_VECTOR) & ConfigMask, + CNOutlineLength, NumCNFeatures, CNFeatures, 0, + CNAdjust[ClassIndex], &CNResult, MatchDebugFlags); + } + } /* ShowBestMatchFor */ diff --git a/classify/adaptmatch.h b/classify/adaptmatch.h new file mode 100644 index 0000000000..6c020ce636 --- /dev/null +++ b/classify/adaptmatch.h @@ -0,0 +1,83 @@ +/****************************************************************************** + ** Filename: adaptmatch.h + ** Purpose: Interface to high-level adaptive matcher + ** Author: Dan Johnson + ** History: Mon Mar 11 11:48:48 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef ADAPTMATCH_H +#define ADAPTMATCH_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "oldlist.h" +#include "tessclas.h" +#include "fxdefs.h" +#include "matchdefs.h" +#include "adaptive.h" +#include "ocrfeatures.h" + +/*--------------------------------------------------------------------------- + Variables +----------------------------------------------------------------------------*/ +extern float GoodAdaptiveMatch; +extern float GreatAdaptiveMatch; +extern int ReliableConfigThreshold; +extern int tess_cn_matching; +extern int tess_bn_matching; +extern int LearningDebugLevel; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +LIST AdaptiveClassifier(TBLOB *Blob, TBLOB *DotBlob, TEXTROW *Row); +/**/ +void AdaptToWord(TWERD *Word, + TEXTROW *Row, + const char *BestChoice, + const char *BestRawChoice, + const char *rejmap); + +void EndAdaptiveClassifier(); + +void InitAdaptiveClassifier(); + +void ResetAdaptiveClassifier(); + +void InitAdaptiveClassifierVars(); + +void PrintAdaptiveStatistics(FILE *File); + +void SettupPass1(); + +void SettupPass2(); + +void MakeNewAdaptedClass(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + ADAPT_TEMPLATES Templates); + +int GetAdaptiveFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_FEATURE_ARRAY IntFeatures, + FEATURE_SET *FloatFeatures); + +int AdaptableWord(TWERD *Word, + const char *BestChoice, + const char *BestRawChoice); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#endif diff --git a/classify/baseline.cpp b/classify/baseline.cpp new file mode 100644 index 0000000000..bcf1141aa8 --- /dev/null +++ b/classify/baseline.cpp @@ -0,0 +1,58 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: baseline.c (Formerly baseline.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 30 16:16:13 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **************************************************************************/ + +/*---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------*/ +#include "baseline.h" +//#include "blobs.h" +#include "debug.h" +#include "hideedge.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef TPOINT SCALE; + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +INT8 baseline_normalized = TRUE; + +make_int_var (baseline_enable, 1, make_baseline_enable, +4, 3, set_baseline_enable, "Baseline Enable"); +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * init_baseline + * + * Initialize the needed baseline variables. + **********************************************************************/ +void init_baseline() { + make_baseline_enable(); + + baseline_normalized = baseline_enable; +} diff --git a/classify/baseline.h b/classify/baseline.h new file mode 100644 index 0000000000..63f2787c02 --- /dev/null +++ b/classify/baseline.h @@ -0,0 +1,91 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: baseline.h (Formerly baseline.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Wed Feb 27 13:39:35 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *************************************************************************/ +#ifndef BASELINE_H +#define BASELINE_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "general.h" +#include "tessclas.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define BASELINE_OFFSET 64 +#define BASELINE_SCALE 128 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern INT8 baseline_normalized; +extern int baseline_enable; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * is_baseline_normalized + * + * Check the baseline_normalized flag to see if it is set. + **********************************************************************/ +#define is_baseline_normalized() \ +(baseline_normalized) + +/********************************************************************** + * reset_baseline_normalized + * + * Reset the baseline_normalized flag to show that it is not being done. + **********************************************************************/ +#define reset_baseline_normalized() \ +(baseline_normalized = FALSE) + +/********************************************************************** + * set_baseline_normalized + * + * Set the baseline_normalized flag to show that it is being done. + **********************************************************************/ +#define set_baseline_normalized() \ +(baseline_normalized = TRUE) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void init_baseline(); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* baseline.c +void init_baseline + _ARGS((void)); + +#undef _ARGS +*/ +#endif diff --git a/classify/blobclass.cpp b/classify/blobclass.cpp new file mode 100644 index 0000000000..a086de0f18 --- /dev/null +++ b/classify/blobclass.cpp @@ -0,0 +1,131 @@ +/****************************************************************************** + ** Filename: blobclass.c + ** Purpose: High level blob classification and training routines. + ** Author: Dan Johnson + ** History: 7/21/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "blobclass.h" +#include "fxdefs.h" +#include "variables.h" +#include "extract.h" +#include "efio.h" +#include "callcpp.h" +#include "chartoname.h" + +#include +#include +#include + +#define MAXFILENAME 80 +#define MAXMATCHES 10 + +// define default font name to be used in training +#define FONT_NAME "UnknownFont" + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* name of current image file being processed */ +extern char imagefile[]; + +/* parameters used to control the training process */ +static const char *FontName = FONT_NAME; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void InitBlobClassifierVars() { +/* + ** Parameters: none + ** Globals: + ** FontName name of font being trained on + ** Operation: Install blob classifier variables into the wiseowl + ** variable system. + ** Return: none + ** Exceptions: none + ** History: Fri Jan 19 16:13:33 1990, DSJ, Created. + */ + VALUE dummy; + + string_variable (FontName, "FontName", FONT_NAME); + +} /* InitBlobClassifierVars */ + + +/*---------------------------------------------------------------------------*/ +void +LearnBlob (TBLOB * Blob, TEXTROW * Row, char BlobText[], int TextLength) +/* + ** Parameters: + ** Blob blob whose micro-features are to be learned + ** Row row of text that blob came from + ** BlobText text that corresponds to blob + ** TextLength number of characters in blob + ** Globals: + ** imagefile base filename of the page being learned + ** FontName name of font currently being trained on + ** Operation: + ** Extract micro-features from the specified blob and append + ** them to the appropriate file. + ** Return: none + ** Exceptions: none + ** History: 7/28/89, DSJ, Created. + */ +#define MAXFILENAME 80 +#define MAXCHARNAME 20 +#define MAXFONTNAME 20 +#define TRAIN_SUFFIX ".tr" +{ + static FILE *FeatureFile = NULL; + char Filename[MAXFILENAME]; + char CharName[MAXCHARNAME]; + CHAR_DESC CharDesc; + LINE_STATS LineStats; + + EnterLearnMode; + + // throw out blobs which do not represent only one character + if (TextLength != 1) + return; + + GetLineStatsFromRow(Row, &LineStats); + + CharDesc = ExtractBlobFeatures (Blob, &LineStats); + + // if a feature file is not yet open, open it + // the name of the file is the name of the image plus TRAIN_SUFFIX + if (FeatureFile == NULL) { + strcpy(Filename, imagefile); + strcat(Filename, TRAIN_SUFFIX); + FeatureFile = Efopen (Filename, "w"); + + cprintf ("TRAINING ... Font name = %s.\n", FontName); + } + + // get the name of the character for this blob + chartoname (CharName, BlobText[0], ""); + + // label the features with a class name and font name + fprintf (FeatureFile, "\n%s %s ", FontName, CharName); + + // write micro-features to file and clean up + WriteCharDescription(FeatureFile, CharDesc); + FreeCharDescription(CharDesc); + +} // LearnBlob diff --git a/classify/blobclass.h b/classify/blobclass.h new file mode 100644 index 0000000000..478ca35ec6 --- /dev/null +++ b/classify/blobclass.h @@ -0,0 +1,49 @@ +/****************************************************************************** + ** Filename: blobclass.h + ** Purpose: Interface to high level classification and training. + ** Author: Dan Johnson + ** History: 5/29/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef BLOBCLASS_H +#define BLOBCLASS_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "oldlist.h" +#include "tessclas.h" + +/*--------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------*/ +/* macros for controlling the display of recognized characters */ +#define EnableCharDisplay() (DisplayCharacters = TRUE) +#define DisableCharDisplay() (DisplayCharacters = FALSE) + +/* macros for controlling the display of the entire match list */ +#define EnableMatchDisplay() (DisplayMatchList = TRUE) +#define DisableMatchDisplay() (DisplayMatchList = FALSE) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void InitBlobClassifierVars(); + +void LearnBlob (TBLOB * Blob, TEXTROW * Row, char BlobText[], int TextLength); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/*parameter used to turn on/off output of recognized chars to the screen */ +#endif diff --git a/classify/chartoname.cpp b/classify/chartoname.cpp new file mode 100644 index 0000000000..0a1d473b6d --- /dev/null +++ b/classify/chartoname.cpp @@ -0,0 +1,74 @@ +/************************************************************************** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. +**************************************************************************/ +#include +#include + +/*chartoname(name,c,dir) converts c into a useful filename*/ +void chartoname(register char *name, /*result */ + char c, /*char to convert */ + const char *dir) { /*directory to use */ + char file[3]; /*filename */ + int index; /*index of namelist */ + static const char *namelist[] = { + "!bang", + "\"doubleq", + "#hash", + "$dollar", + "%percent", + "&and", + "'quote", + "(lround", + ")rround", + "*asterisk", + "+plus", + ",comma", + "-minus", + ".dot", + "/slash", + ":colon", + ";semic", + "greater", + "?question", + "@at", + "[lsquare", + "\\backsl", + "]rsquare", + "^uparr", + "_unders", + "`grave", + "{lbrace", + "|bar", + "}rbrace", + "~tilde" + }; + + strcpy(name, dir); /*add specific directory */ + for (index = 0; index < sizeof namelist / sizeof (char *) + && c != namelist[index][0]; index++); + if (index < sizeof namelist / sizeof (char *)) + /*add text name */ + strcat (name, &namelist[index][1]); + else { + if (isupper (c)) { + file[0] = 'c'; /*direct a-z or A-Z */ + file[1] = c; /*direct a-z or A-Z */ + file[2] = '\0'; + } + else { + file[0] = c; /*direct a-z or A-Z */ + file[1] = '\0'; + } + strcat(name, file); /*append filename */ + } +} diff --git a/classify/chartoname.h b/classify/chartoname.h new file mode 100644 index 0000000000..07ded25f33 --- /dev/null +++ b/classify/chartoname.h @@ -0,0 +1,21 @@ +#ifndef CHARTONAME_H +#define CHARTONAME_H + + /*result */ +void chartoname(register char *name, + char c, /*char to convert */ + const char *dir); /*directory to use */ + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _P(s) s +#else +# define _P(s) () +#endif*/ + +/* chartoname.c +int chartoname _P((char *name, int c, char *dir)); + +#undef _P +*/ +#endif diff --git a/classify/cluster.cpp b/classify/cluster.cpp new file mode 100644 index 0000000000..c88749f64d --- /dev/null +++ b/classify/cluster.cpp @@ -0,0 +1,2786 @@ +/****************************************************************************** + ** Filename: cluster.c + ** Purpose: Routines for clustering points in N-D space + ** Author: Dan Johnson + ** History: 5/29/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#include "oldheap.h" +#include "const.h" +#include "cluster.h" +#include "emalloc.h" +#include "danerror.h" +#include "freelist.h" +#include + +#define HOTELLING 1 // If true use Hotelling's test to decide where to split. +#define FTABLE_X 10 // Size of FTable. +#define FTABLE_Y 100 // Size of FTable. + +// Table of values approximating the cumulative F-distribution for a confidence of 1%. +double FTable[FTABLE_Y][FTABLE_X] = { + {4052.19, 4999.52, 5403.34, 5624.62, 5763.65, 5858.97, 5928.33, 5981.10, 6022.50, 6055.85,}, + {98.502, 99.000, 99.166, 99.249, 99.300, 99.333, 99.356, 99.374, 99.388, 99.399,}, + {34.116, 30.816, 29.457, 28.710, 28.237, 27.911, 27.672, 27.489, 27.345, 27.229,}, + {21.198, 18.000, 16.694, 15.977, 15.522, 15.207, 14.976, 14.799, 14.659, 14.546,}, + {16.258, 13.274, 12.060, 11.392, 10.967, 10.672, 10.456, 10.289, 10.158, 10.051,}, + {13.745, 10.925, 9.780, 9.148, 8.746, 8.466, 8.260, 8.102, 7.976, 7.874,}, + {12.246, 9.547, 8.451, 7.847, 7.460, 7.191, 6.993, 6.840, 6.719, 6.620,}, + {11.259, 8.649, 7.591, 7.006, 6.632, 6.371, 6.178, 6.029, 5.911, 5.814,}, + {10.561, 8.022, 6.992, 6.422, 6.057, 5.802, 5.613, 5.467, 5.351, 5.257,}, + {10.044, 7.559, 6.552, 5.994, 5.636, 5.386, 5.200, 5.057, 4.942, 4.849,}, + { 9.646, 7.206, 6.217, 5.668, 5.316, 5.069, 4.886, 4.744, 4.632, 4.539,}, + { 9.330, 6.927, 5.953, 5.412, 5.064, 4.821, 4.640, 4.499, 4.388, 4.296,}, + { 9.074, 6.701, 5.739, 5.205, 4.862, 4.620, 4.441, 4.302, 4.191, 4.100,}, + { 8.862, 6.515, 5.564, 5.035, 4.695, 4.456, 4.278, 4.140, 4.030, 3.939,}, + { 8.683, 6.359, 5.417, 4.893, 4.556, 4.318, 4.142, 4.004, 3.895, 3.805,}, + { 8.531, 6.226, 5.292, 4.773, 4.437, 4.202, 4.026, 3.890, 3.780, 3.691,}, + { 8.400, 6.112, 5.185, 4.669, 4.336, 4.102, 3.927, 3.791, 3.682, 3.593,}, + { 8.285, 6.013, 5.092, 4.579, 4.248, 4.015, 3.841, 3.705, 3.597, 3.508,}, + { 8.185, 5.926, 5.010, 4.500, 4.171, 3.939, 3.765, 3.631, 3.523, 3.434,}, + { 8.096, 5.849, 4.938, 4.431, 4.103, 3.871, 3.699, 3.564, 3.457, 3.368,}, + { 8.017, 5.780, 4.874, 4.369, 4.042, 3.812, 3.640, 3.506, 3.398, 3.310,}, + { 7.945, 5.719, 4.817, 4.313, 3.988, 3.758, 3.587, 3.453, 3.346, 3.258,}, + { 7.881, 5.664, 4.765, 4.264, 3.939, 3.710, 3.539, 3.406, 3.299, 3.211,}, + { 7.823, 5.614, 4.718, 4.218, 3.895, 3.667, 3.496, 3.363, 3.256, 3.168,}, + { 7.770, 5.568, 4.675, 4.177, 3.855, 3.627, 3.457, 3.324, 3.217, 3.129,}, + { 7.721, 5.526, 4.637, 4.140, 3.818, 3.591, 3.421, 3.288, 3.182, 3.094,}, + { 7.677, 5.488, 4.601, 4.106, 3.785, 3.558, 3.388, 3.256, 3.149, 3.062,}, + { 7.636, 5.453, 4.568, 4.074, 3.754, 3.528, 3.358, 3.226, 3.120, 3.032,}, + { 7.598, 5.420, 4.538, 4.045, 3.725, 3.499, 3.330, 3.198, 3.092, 3.005,}, + { 7.562, 5.390, 4.510, 4.018, 3.699, 3.473, 3.305, 3.173, 3.067, 2.979,}, + { 7.530, 5.362, 4.484, 3.993, 3.675, 3.449, 3.281, 3.149, 3.043, 2.955,}, + { 7.499, 5.336, 4.459, 3.969, 3.652, 3.427, 3.258, 3.127, 3.021, 2.934,}, + { 7.471, 5.312, 4.437, 3.948, 3.630, 3.406, 3.238, 3.106, 3.000, 2.913,}, + { 7.444, 5.289, 4.416, 3.927, 3.611, 3.386, 3.218, 3.087, 2.981, 2.894,}, + { 7.419, 5.268, 4.396, 3.908, 3.592, 3.368, 3.200, 3.069, 2.963, 2.876,}, + { 7.396, 5.248, 4.377, 3.890, 3.574, 3.351, 3.183, 3.052, 2.946, 2.859,}, + { 7.373, 5.229, 4.360, 3.873, 3.558, 3.334, 3.167, 3.036, 2.930, 2.843,}, + { 7.353, 5.211, 4.343, 3.858, 3.542, 3.319, 3.152, 3.021, 2.915, 2.828,}, + { 7.333, 5.194, 4.327, 3.843, 3.528, 3.305, 3.137, 3.006, 2.901, 2.814,}, + { 7.314, 5.179, 4.313, 3.828, 3.514, 3.291, 3.124, 2.993, 2.888, 2.801,}, + { 7.296, 5.163, 4.299, 3.815, 3.501, 3.278, 3.111, 2.980, 2.875, 2.788,}, + { 7.280, 5.149, 4.285, 3.802, 3.488, 3.266, 3.099, 2.968, 2.863, 2.776,}, + { 7.264, 5.136, 4.273, 3.790, 3.476, 3.254, 3.087, 2.957, 2.851, 2.764,}, + { 7.248, 5.123, 4.261, 3.778, 3.465, 3.243, 3.076, 2.946, 2.840, 2.754,}, + { 7.234, 5.110, 4.249, 3.767, 3.454, 3.232, 3.066, 2.935, 2.830, 2.743,}, + { 7.220, 5.099, 4.238, 3.757, 3.444, 3.222, 3.056, 2.925, 2.820, 2.733,}, + { 7.207, 5.087, 4.228, 3.747, 3.434, 3.213, 3.046, 2.916, 2.811, 2.724,}, + { 7.194, 5.077, 4.218, 3.737, 3.425, 3.204, 3.037, 2.907, 2.802, 2.715,}, + { 7.182, 5.066, 4.208, 3.728, 3.416, 3.195, 3.028, 2.898, 2.793, 2.706,}, + { 7.171, 5.057, 4.199, 3.720, 3.408, 3.186, 3.020, 2.890, 2.785, 2.698,}, + { 7.159, 5.047, 4.191, 3.711, 3.400, 3.178, 3.012, 2.882, 2.777, 2.690,}, + { 7.149, 5.038, 4.182, 3.703, 3.392, 3.171, 3.005, 2.874, 2.769, 2.683,}, + { 7.139, 5.030, 4.174, 3.695, 3.384, 3.163, 2.997, 2.867, 2.762, 2.675,}, + { 7.129, 5.021, 4.167, 3.688, 3.377, 3.156, 2.990, 2.860, 2.755, 2.668,}, + { 7.119, 5.013, 4.159, 3.681, 3.370, 3.149, 2.983, 2.853, 2.748, 2.662,}, + { 7.110, 5.006, 4.152, 3.674, 3.363, 3.143, 2.977, 2.847, 2.742, 2.655,}, + { 7.102, 4.998, 4.145, 3.667, 3.357, 3.136, 2.971, 2.841, 2.736, 2.649,}, + { 7.093, 4.991, 4.138, 3.661, 3.351, 3.130, 2.965, 2.835, 2.730, 2.643,}, + { 7.085, 4.984, 4.132, 3.655, 3.345, 3.124, 2.959, 2.829, 2.724, 2.637,}, + { 7.077, 4.977, 4.126, 3.649, 3.339, 3.119, 2.953, 2.823, 2.718, 2.632,}, + { 7.070, 4.971, 4.120, 3.643, 3.333, 3.113, 2.948, 2.818, 2.713, 2.626,}, + { 7.062, 4.965, 4.114, 3.638, 3.328, 3.108, 2.942, 2.813, 2.708, 2.621,}, + { 7.055, 4.959, 4.109, 3.632, 3.323, 3.103, 2.937, 2.808, 2.703, 2.616,}, + { 7.048, 4.953, 4.103, 3.627, 3.318, 3.098, 2.932, 2.803, 2.698, 2.611,}, + { 7.042, 4.947, 4.098, 3.622, 3.313, 3.093, 2.928, 2.798, 2.693, 2.607,}, + { 7.035, 4.942, 4.093, 3.618, 3.308, 3.088, 2.923, 2.793, 2.689, 2.602,}, + { 7.029, 4.937, 4.088, 3.613, 3.304, 3.084, 2.919, 2.789, 2.684, 2.598,}, + { 7.023, 4.932, 4.083, 3.608, 3.299, 3.080, 2.914, 2.785, 2.680, 2.593,}, + { 7.017, 4.927, 4.079, 3.604, 3.295, 3.075, 2.910, 2.781, 2.676, 2.589,}, + { 7.011, 4.922, 4.074, 3.600, 3.291, 3.071, 2.906, 2.777, 2.672, 2.585,}, + { 7.006, 4.917, 4.070, 3.596, 3.287, 3.067, 2.902, 2.773, 2.668, 2.581,}, + { 7.001, 4.913, 4.066, 3.591, 3.283, 3.063, 2.898, 2.769, 2.664, 2.578,}, + { 6.995, 4.908, 4.062, 3.588, 3.279, 3.060, 2.895, 2.765, 2.660, 2.574,}, + { 6.990, 4.904, 4.058, 3.584, 3.275, 3.056, 2.891, 2.762, 2.657, 2.570,}, + { 6.985, 4.900, 4.054, 3.580, 3.272, 3.052, 2.887, 2.758, 2.653, 2.567,}, + { 6.981, 4.896, 4.050, 3.577, 3.268, 3.049, 2.884, 2.755, 2.650, 2.563,}, + { 6.976, 4.892, 4.047, 3.573, 3.265, 3.046, 2.881, 2.751, 2.647, 2.560,}, + { 6.971, 4.888, 4.043, 3.570, 3.261, 3.042, 2.877, 2.748, 2.644, 2.557,}, + { 6.967, 4.884, 4.040, 3.566, 3.258, 3.039, 2.874, 2.745, 2.640, 2.554,}, + { 6.963, 4.881, 4.036, 3.563, 3.255, 3.036, 2.871, 2.742, 2.637, 2.551,}, + { 6.958, 4.877, 4.033, 3.560, 3.252, 3.033, 2.868, 2.739, 2.634, 2.548,}, + { 6.954, 4.874, 4.030, 3.557, 3.249, 3.030, 2.865, 2.736, 2.632, 2.545,}, + { 6.950, 4.870, 4.027, 3.554, 3.246, 3.027, 2.863, 2.733, 2.629, 2.542,}, + { 6.947, 4.867, 4.024, 3.551, 3.243, 3.025, 2.860, 2.731, 2.626, 2.539,}, + { 6.943, 4.864, 4.021, 3.548, 3.240, 3.022, 2.857, 2.728, 2.623, 2.537,}, + { 6.939, 4.861, 4.018, 3.545, 3.238, 3.019, 2.854, 2.725, 2.621, 2.534,}, + { 6.935, 4.858, 4.015, 3.543, 3.235, 3.017, 2.852, 2.723, 2.618, 2.532,}, + { 6.932, 4.855, 4.012, 3.540, 3.233, 3.014, 2.849, 2.720, 2.616, 2.529,}, + { 6.928, 4.852, 4.010, 3.538, 3.230, 3.012, 2.847, 2.718, 2.613, 2.527,}, + { 6.925, 4.849, 4.007, 3.535, 3.228, 3.009, 2.845, 2.715, 2.611, 2.524,}, + { 6.922, 4.846, 4.004, 3.533, 3.225, 3.007, 2.842, 2.713, 2.609, 2.522,}, + { 6.919, 4.844, 4.002, 3.530, 3.223, 3.004, 2.840, 2.711, 2.606, 2.520,}, + { 6.915, 4.841, 3.999, 3.528, 3.221, 3.002, 2.838, 2.709, 2.604, 2.518,}, + { 6.912, 4.838, 3.997, 3.525, 3.218, 3.000, 2.835, 2.706, 2.602, 2.515,}, + { 6.909, 4.836, 3.995, 3.523, 3.216, 2.998, 2.833, 2.704, 2.600, 2.513,}, + { 6.906, 4.833, 3.992, 3.521, 3.214, 2.996, 2.831, 2.702, 2.598, 2.511,}, + { 6.904, 4.831, 3.990, 3.519, 3.212, 2.994, 2.829, 2.700, 2.596, 2.509,}, + { 6.901, 4.829, 3.988, 3.517, 3.210, 2.992, 2.827, 2.698, 2.594, 2.507,}, + { 6.898, 4.826, 3.986, 3.515, 3.208, 2.990, 2.825, 2.696, 2.592, 2.505,}, + { 6.895, 4.824, 3.984, 3.513, 3.206, 2.988, 2.823, 2.694, 2.590, 2.503} +}; + +/* define the variance which will be used as a minimum variance for any + dimension of any feature. Since most features are calculated from numbers + with a precision no better than 1 in 128, the variance should never be + less than the square of this number for parameters whose range is 1. */ +#define MINVARIANCE 0.0001 + +/* define the absolute minimum number of samples which must be present in + order to accurately test hypotheses about underlying probability + distributions. Define separately the minimum samples that are needed + before a statistical analysis is attempted; this number should be + equal to MINSAMPLES but can be set to a lower number for early testing + when very few samples are available. */ +#define MINBUCKETS 5 +#define MINSAMPLESPERBUCKET 5 +#define MINSAMPLES (MINBUCKETS * MINSAMPLESPERBUCKET) +#define MINSAMPLESNEEDED 1 + +/* define the size of the table which maps normalized samples to + histogram buckets. Also define the number of standard deviations + in a normal distribution which are considered to be significant. + The mapping table will be defined in such a way that it covers + the specified number of standard deviations on either side of + the mean. BUCKETTABLESIZE should always be even. */ +#define BUCKETTABLESIZE 1024 +#define NORMALEXTENT 3.0 + +typedef struct +{ + CLUSTER *Cluster; + CLUSTER *Neighbor; +} + + +TEMPCLUSTER; + +typedef struct +{ + FLOAT32 AvgVariance; + FLOAT32 *CoVariance; + FLOAT32 *Min; // largest negative distance from the mean + FLOAT32 *Max; // largest positive distance from the mean +} + + +STATISTICS; + +typedef struct +{ + DISTRIBUTION Distribution; // distribution being tested for + UINT32 SampleCount; // # of samples in histogram + FLOAT64 Confidence; // confidence level of test + FLOAT64 ChiSquared; // test threshold + UINT16 NumberOfBuckets; // number of cells in histogram + UINT16 Bucket[BUCKETTABLESIZE];// mapping to histogram buckets + UINT32 *Count; // frequency of occurence histogram + FLOAT32 *ExpectedCount; // expected histogram +} + + +BUCKETS; + +typedef struct +{ + UINT16 DegreesOfFreedom; + FLOAT64 Alpha; + FLOAT64 ChiSquared; +} + + +CHISTRUCT; + +typedef FLOAT64 (*DENSITYFUNC) (INT32); +typedef FLOAT64 (*SOLVEFUNC) (CHISTRUCT *, double); + +#define Odd(N) ((N)%2) +#define Mirror(N,R) ((R) - (N) - 1) +#define Abs(N) ( ( (N) < 0 ) ? ( -(N) ) : (N) ) + +//--------------Global Data Definitions and Declarations---------------------- +/* the following variables are declared as global so that routines which +are called from the kd-tree walker can get to them. */ +static HEAP *Heap; +static TEMPCLUSTER *TempCluster; +static KDTREE *Tree; +static INT32 CurrentTemp; + +/* the following variables describe a discrete normal distribution + which is used by NormalDensity() and NormalBucket(). The + constant NORMALEXTENT determines how many standard + deviations of the distribution are mapped onto the fixed + discrete range of x. x=0 is mapped to -NORMALEXTENT standard + deviations and x=BUCKETTABLESIZE is mapped to + +NORMALEXTENT standard deviations. */ +#define SqrtOf2Pi 2.506628275 +static FLOAT64 NormalStdDev = BUCKETTABLESIZE / (2.0 * NORMALEXTENT); +static FLOAT64 NormalVariance = +(BUCKETTABLESIZE * BUCKETTABLESIZE) / (4.0 * NORMALEXTENT * NORMALEXTENT); +static FLOAT64 NormalMagnitude = +(2.0 * NORMALEXTENT) / (SqrtOf2Pi * BUCKETTABLESIZE); +static FLOAT64 NormalMean = BUCKETTABLESIZE / 2; + +// keep a list of histogram buckets to minimize recomputing them +static LIST OldBuckets[] = { NIL, NIL, NIL }; + +/* define lookup tables used to compute the number of histogram buckets + that should be used for a given number of samples. */ +#define LOOKUPTABLESIZE 8 +#define MAXBUCKETS 39 +#define MAXDEGREESOFFREEDOM MAXBUCKETS + +static UINT32 CountTable[LOOKUPTABLESIZE] = { + MINSAMPLES, 200, 400, 600, 800, 1000, 1500, 2000 +}; +static UINT16 BucketsTable[LOOKUPTABLESIZE] = { + MINBUCKETS, 16, 20, 24, 27, 30, 35, MAXBUCKETS +}; + +/*------------------------------------------------------------------------- + Private Function Prototypes +--------------------------------------------------------------------------*/ +void CreateClusterTree(CLUSTERER *Clusterer); + +void MakePotentialClusters(CLUSTER *Cluster, VISIT Order, INT32 Level); + +CLUSTER *FindNearestNeighbor(KDTREE *Tree, + CLUSTER *Cluster, + FLOAT32 *Distance); + +CLUSTER *MakeNewCluster(CLUSTERER *Clusterer, TEMPCLUSTER *TempCluster); + +INT32 MergeClusters (INT16 N, +register PARAM_DESC ParamDesc[], +register INT32 n1, +register INT32 n2, +register FLOAT32 m[], +register FLOAT32 m1[], register FLOAT32 m2[]); + +void ComputePrototypes(CLUSTERER *Clusterer, CLUSTERCONFIG *Config); + +PROTOTYPE *MakePrototype(CLUSTERER *Clusterer, + CLUSTERCONFIG *Config, + CLUSTER *Cluster); + +PROTOTYPE *MakeDegenerateProto(UINT16 N, + CLUSTER *Cluster, + STATISTICS *Statistics, + PROTOSTYLE Style, + INT32 MinSamples); + +PROTOTYPE *TestEllipticalProto(CLUSTERER *Clusterer, + CLUSTER *Cluster, + STATISTICS *Statistics); + +PROTOTYPE *MakeSphericalProto(CLUSTERER *Clusterer, + CLUSTER *Cluster, + STATISTICS *Statistics, + BUCKETS *Buckets); + +PROTOTYPE *MakeEllipticalProto(CLUSTERER *Clusterer, + CLUSTER *Cluster, + STATISTICS *Statistics, + BUCKETS *Buckets); + +PROTOTYPE *MakeMixedProto(CLUSTERER *Clusterer, + CLUSTER *Cluster, + STATISTICS *Statistics, + BUCKETS *NormalBuckets, + FLOAT64 Confidence); + +void MakeDimRandom(UINT16 i, PROTOTYPE *Proto, PARAM_DESC *ParamDesc); + +void MakeDimUniform(UINT16 i, PROTOTYPE *Proto, STATISTICS *Statistics); + +STATISTICS *ComputeStatistics (INT16 N, +PARAM_DESC ParamDesc[], CLUSTER * Cluster); + +PROTOTYPE *NewSphericalProto(UINT16 N, + CLUSTER *Cluster, + STATISTICS *Statistics); + +PROTOTYPE *NewEllipticalProto(INT16 N, + CLUSTER *Cluster, + STATISTICS *Statistics); + +PROTOTYPE *NewMixedProto(INT16 N, CLUSTER *Cluster, STATISTICS *Statistics); + +PROTOTYPE *NewSimpleProto(INT16 N, CLUSTER *Cluster); + +BOOL8 Independent (PARAM_DESC ParamDesc[], +INT16 N, FLOAT32 * CoVariance, FLOAT32 Independence); + +BUCKETS *GetBuckets(DISTRIBUTION Distribution, + UINT32 SampleCount, + FLOAT64 Confidence); + +BUCKETS *MakeBuckets(DISTRIBUTION Distribution, + UINT32 SampleCount, + FLOAT64 Confidence); + +UINT16 OptimumNumberOfBuckets(UINT32 SampleCount); + +FLOAT64 ComputeChiSquared(UINT16 DegreesOfFreedom, FLOAT64 Alpha); + +FLOAT64 NormalDensity(INT32 x); + +FLOAT64 UniformDensity(INT32 x); + +FLOAT64 Integral(FLOAT64 f1, FLOAT64 f2, FLOAT64 Dx); + +void FillBuckets(BUCKETS *Buckets, + CLUSTER *Cluster, + UINT16 Dim, + PARAM_DESC *ParamDesc, + FLOAT32 Mean, + FLOAT32 StdDev); + +UINT16 NormalBucket(PARAM_DESC *ParamDesc, + FLOAT32 x, + FLOAT32 Mean, + FLOAT32 StdDev); + +UINT16 UniformBucket(PARAM_DESC *ParamDesc, + FLOAT32 x, + FLOAT32 Mean, + FLOAT32 StdDev); + +BOOL8 DistributionOK(BUCKETS *Buckets); + +void FreeStatistics(STATISTICS *Statistics); + +void FreeBuckets(BUCKETS *Buckets); + +void FreeCluster(CLUSTER *Cluster); + +UINT16 DegreesOfFreedom(DISTRIBUTION Distribution, UINT16 HistogramBuckets); + +int NumBucketsMatch(void *arg1, //BUCKETS *Histogram, + void *arg2); //UINT16 *DesiredNumberOfBuckets); + +int ListEntryMatch(void *arg1, void *arg2); + +void AdjustBuckets(BUCKETS *Buckets, UINT32 NewSampleCount); + +void InitBuckets(BUCKETS *Buckets); + +int AlphaMatch(void *arg1, //CHISTRUCT *ChiStruct, + void *arg2); //CHISTRUCT *SearchKey); + +CHISTRUCT *NewChiStruct(UINT16 DegreesOfFreedom, FLOAT64 Alpha); + +FLOAT64 Solve(SOLVEFUNC Function, + void *FunctionParams, + FLOAT64 InitialGuess, + FLOAT64 Accuracy); + +FLOAT64 ChiArea(CHISTRUCT *ChiParams, FLOAT64 x); + +BOOL8 MultipleCharSamples(CLUSTERER *Clusterer, + CLUSTER *Cluster, + FLOAT32 MaxIllegal); + +double InvertMatrix(const float* input, int size, float* inv); + +//--------------------------Public Code-------------------------------------- +/** MakeClusterer ********************************************************** +Parameters: SampleSize number of dimensions in feature space + ParamDesc description of each dimension +Globals: None +Operation: This routine creates a new clusterer data structure, + initializes it, and returns a pointer to it. +Return: pointer to the new clusterer data structure +Exceptions: None +History: 5/29/89, DSJ, Created. +****************************************************************************/ +CLUSTERER * +MakeClusterer (INT16 SampleSize, PARAM_DESC ParamDesc[]) { + CLUSTERER *Clusterer; + int i; + + // allocate main clusterer data structure and init simple fields + Clusterer = (CLUSTERER *) Emalloc (sizeof (CLUSTERER)); + Clusterer->SampleSize = SampleSize; + Clusterer->NumberOfSamples = 0; + Clusterer->NumChar = 0; + + // init fields which will not be used initially + Clusterer->Root = NULL; + Clusterer->ProtoList = NIL; + + // maintain a copy of param descriptors in the clusterer data structure + Clusterer->ParamDesc = + (PARAM_DESC *) Emalloc (SampleSize * sizeof (PARAM_DESC)); + for (i = 0; i < SampleSize; i++) { + Clusterer->ParamDesc[i].Circular = ParamDesc[i].Circular; + Clusterer->ParamDesc[i].NonEssential = ParamDesc[i].NonEssential; + Clusterer->ParamDesc[i].Min = ParamDesc[i].Min; + Clusterer->ParamDesc[i].Max = ParamDesc[i].Max; + Clusterer->ParamDesc[i].Range = ParamDesc[i].Max - ParamDesc[i].Min; + Clusterer->ParamDesc[i].HalfRange = Clusterer->ParamDesc[i].Range / 2; + Clusterer->ParamDesc[i].MidRange = + (ParamDesc[i].Max + ParamDesc[i].Min) / 2; + } + + // allocate a kd tree to hold the samples + Clusterer->KDTree = MakeKDTree (SampleSize, ParamDesc); + + // execute hook for monitoring clustering operation + // (*ClustererCreationHook)( Clusterer ); + + return (Clusterer); +} // MakeClusterer + + +/** MakeSample *********************************************************** +Parameters: Clusterer clusterer data structure to add sample to + Feature feature to be added to clusterer + CharID unique ident. of char that sample came from +Globals: None +Operation: This routine creates a new sample data structure to hold + the specified feature. This sample is added to the clusterer + data structure (so that it knows which samples are to be + clustered later), and a pointer to the sample is returned to + the caller. +Return: Pointer to the new sample data structure +Exceptions: ALREADYCLUSTERED MakeSample can't be called after + ClusterSamples has been called +History: 5/29/89, DSJ, Created. +*****************************************************************************/ +SAMPLE * +MakeSample (CLUSTERER * Clusterer, FLOAT32 Feature[], INT32 CharID) { + SAMPLE *Sample; + int i; + + // see if the samples have already been clustered - if so trap an error + if (Clusterer->Root != NULL) + DoError (ALREADYCLUSTERED, + "Can't add samples after they have been clustered"); + + // allocate the new sample and initialize it + Sample = (SAMPLE *) Emalloc (sizeof (SAMPLE) + + (Clusterer->SampleSize - + 1) * sizeof (FLOAT32)); + Sample->Clustered = FALSE; + Sample->Prototype = FALSE; + Sample->SampleCount = 1; + Sample->Left = NULL; + Sample->Right = NULL; + Sample->CharID = CharID; + + for (i = 0; i < Clusterer->SampleSize; i++) + Sample->Mean[i] = Feature[i]; + + // add the sample to the KD tree - keep track of the total # of samples + Clusterer->NumberOfSamples++; + KDStore (Clusterer->KDTree, Sample->Mean, (char *) Sample); + if (CharID >= Clusterer->NumChar) + Clusterer->NumChar = CharID + 1; + + // execute hook for monitoring clustering operation + // (*SampleCreationHook)( Sample ); + + return (Sample); +} // MakeSample + + +/** ClusterSamples *********************************************************** +Parameters: Clusterer data struct containing samples to be clustered + Config parameters which control clustering process +Globals: None +Operation: This routine first checks to see if the samples in this + clusterer have already been clustered before; if so, it does + not bother to recreate the cluster tree. It simply recomputes + the prototypes based on the new Config info. + If the samples have not been clustered before, the + samples in the KD tree are formed into a cluster tree and then + the prototypes are computed from the cluster tree. + In either case this routine returns a pointer to a + list of prototypes that best represent the samples given + the constraints specified in Config. +Return: Pointer to a list of prototypes +Exceptions: None +History: 5/29/89, DSJ, Created. +*******************************************************************************/ +LIST ClusterSamples(CLUSTERER *Clusterer, CLUSTERCONFIG *Config) { + //only create cluster tree if samples have never been clustered before + if (Clusterer->Root == NULL) + CreateClusterTree(Clusterer); + + //deallocate the old prototype list if one exists + FreeProtoList (&Clusterer->ProtoList); + Clusterer->ProtoList = NIL; + + //compute prototypes starting at the root node in the tree + ComputePrototypes(Clusterer, Config); + return (Clusterer->ProtoList); +} // ClusterSamples + + +/** FreeClusterer ************************************************************* +Parameters: Clusterer pointer to data structure to be freed +Globals: None +Operation: This routine frees all of the memory allocated to the + specified data structure. It will not, however, free + the memory used by the prototype list. The pointers to + the clusters for each prototype in the list will be set + to NULL to indicate that the cluster data structures no + longer exist. Any sample lists that have been obtained + via calls to GetSamples are no longer valid. +Return: None +Exceptions: None +History: 6/6/89, DSJ, Created. +*******************************************************************************/ +void FreeClusterer(CLUSTERER *Clusterer) { + if (Clusterer != NULL) { + memfree (Clusterer->ParamDesc); + if (Clusterer->KDTree != NULL) + FreeKDTree (Clusterer->KDTree); + if (Clusterer->Root != NULL) + FreeCluster (Clusterer->Root); + iterate (Clusterer->ProtoList) { + ((PROTOTYPE *) (first (Clusterer->ProtoList)))->Cluster = NULL; + } + memfree(Clusterer); + } +} // FreeClusterer + + +/** FreeProtoList ************************************************************ +Parameters: ProtoList pointer to list of prototypes to be freed +Globals: None +Operation: This routine frees all of the memory allocated to the + specified list of prototypes. The clusters which are + pointed to by the prototypes are not freed. +Return: None +Exceptions: None +History: 6/6/89, DSJ, Created. +*****************************************************************************/ +void FreeProtoList(LIST *ProtoList) { + destroy_nodes(*ProtoList, FreePrototype); +} // FreeProtoList + + +/** FreePrototype ************************************************************ +Parameters: Prototype prototype data structure to be deallocated +Globals: None +Operation: This routine deallocates the memory consumed by the specified + prototype and modifies the corresponding cluster so that it + is no longer marked as a prototype. The cluster is NOT + deallocated by this routine. +Return: None +Exceptions: None +History: 5/30/89, DSJ, Created. +*******************************************************************************/ +void FreePrototype(void *arg) { //PROTOTYPE *Prototype) + PROTOTYPE *Prototype = (PROTOTYPE *) arg; + + // unmark the corresponding cluster (if there is one + if (Prototype->Cluster != NULL) + Prototype->Cluster->Prototype = FALSE; + + // deallocate the prototype statistics and then the prototype itself + if (Prototype->Distrib != NULL) + memfree (Prototype->Distrib); + if (Prototype->Mean != NULL) + memfree (Prototype->Mean); + if (Prototype->Style != spherical) { + if (Prototype->Variance.Elliptical != NULL) + memfree (Prototype->Variance.Elliptical); + if (Prototype->Magnitude.Elliptical != NULL) + memfree (Prototype->Magnitude.Elliptical); + if (Prototype->Weight.Elliptical != NULL) + memfree (Prototype->Weight.Elliptical); + } + memfree(Prototype); +} // FreePrototype + + +/** NextSample ************************************************************ +Parameters: SearchState ptr to list containing clusters to be searched +Globals: None +Operation: This routine is used to find all of the samples which + belong to a cluster. It starts by removing the top + cluster on the cluster list (SearchState). If this cluster is + a leaf it is returned. Otherwise, the right subcluster + is pushed on the list and we continue the search in the + left subcluster. This continues until a leaf is found. + If all samples have been found, NULL is returned. + InitSampleSearch() must be called + before NextSample() to initialize the search. +Return: Pointer to the next leaf cluster (sample) or NULL. +Exceptions: None +History: 6/16/89, DSJ, Created. +****************************************************************************/ +CLUSTER *NextSample(LIST *SearchState) { + CLUSTER *Cluster; + + if (*SearchState == NIL) + return (NULL); + Cluster = (CLUSTER *) first (*SearchState); + *SearchState = pop (*SearchState); + while (TRUE) { + if (Cluster->Left == NULL) + return (Cluster); + *SearchState = push (*SearchState, Cluster->Right); + Cluster = Cluster->Left; + } +} // NextSample + + +/** Mean *********************************************************** +Parameters: Proto prototype to return mean of + Dimension dimension whose mean is to be returned +Globals: none +Operation: This routine returns the mean of the specified + prototype in the indicated dimension. +Return: Mean of Prototype in Dimension +Exceptions: none +History: 7/6/89, DSJ, Created. +*********************************************************************/ +FLOAT32 Mean(PROTOTYPE *Proto, UINT16 Dimension) { + return (Proto->Mean[Dimension]); +} // Mean + + +/** StandardDeviation ************************************************* +Parameters: Proto prototype to return standard deviation of + Dimension dimension whose stddev is to be returned +Globals: none +Operation: This routine returns the standard deviation of the + prototype in the indicated dimension. +Return: Standard deviation of Prototype in Dimension +Exceptions: none +History: 7/6/89, DSJ, Created. +**********************************************************************/ +FLOAT32 StandardDeviation(PROTOTYPE *Proto, UINT16 Dimension) { + switch (Proto->Style) { + case spherical: + return ((FLOAT32) sqrt ((double) Proto->Variance.Spherical)); + case elliptical: + return ((FLOAT32) + sqrt ((double) Proto->Variance.Elliptical[Dimension])); + case mixed: + switch (Proto->Distrib[Dimension]) { + case normal: + return ((FLOAT32) + sqrt ((double) Proto->Variance.Elliptical[Dimension])); + case uniform: + case D_random: + return (Proto->Variance.Elliptical[Dimension]); + } + } + return 0.0f; +} // StandardDeviation + + +/*--------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------*/ +/** CreateClusterTree ******************************************************* +Parameters: Clusterer data structure holdings samples to be clustered +Globals: Tree kd-tree holding samples + TempCluster array of temporary clusters + CurrentTemp index of next temp cluster to be used + Heap heap used to hold temp clusters - "best" on top +Operation: This routine performs a bottoms-up clustering on the samples + held in the kd-tree of the Clusterer data structure. The + result is a cluster tree. Each node in the tree represents + a cluster which conceptually contains a subset of the samples. + More precisely, the cluster contains all of the samples which + are contained in its two sub-clusters. The leaves of the + tree are the individual samples themselves; they have no + sub-clusters. The root node of the tree conceptually contains + all of the samples. +Return: None (the Clusterer data structure is changed) +Exceptions: None +History: 5/29/89, DSJ, Created. +******************************************************************************/ +void CreateClusterTree(CLUSTERER *Clusterer) { + HEAPENTRY HeapEntry; + TEMPCLUSTER *PotentialCluster; + + // save the kd-tree in a global variable so kd-tree walker can get at it + Tree = Clusterer->KDTree; + + // allocate memory to to hold all of the "potential" clusters + TempCluster = (TEMPCLUSTER *) + Emalloc (Clusterer->NumberOfSamples * sizeof (TEMPCLUSTER)); + CurrentTemp = 0; + + // each sample and its nearest neighbor form a "potential" cluster + // save these in a heap with the "best" potential clusters on top + Heap = MakeHeap (Clusterer->NumberOfSamples); + KDWalk (Tree, (void_proc) MakePotentialClusters); + + // form potential clusters into actual clusters - always do "best" first + while (GetTopOfHeap (Heap, &HeapEntry) != EMPTY) { + PotentialCluster = (TEMPCLUSTER *) (HeapEntry.Data); + + // if main cluster of potential cluster is already in another cluster + // then we don't need to worry about it + if (PotentialCluster->Cluster->Clustered) { + continue; + } + + // if main cluster is not yet clustered, but its nearest neighbor is + // then we must find a new nearest neighbor + else if (PotentialCluster->Neighbor->Clustered) { + PotentialCluster->Neighbor = + FindNearestNeighbor (Tree, PotentialCluster->Cluster, + &(HeapEntry.Key)); + if (PotentialCluster->Neighbor != NULL) { + HeapStore(Heap, &HeapEntry); + } + } + + // if neither cluster is already clustered, form permanent cluster + else { + PotentialCluster->Cluster = + MakeNewCluster(Clusterer, PotentialCluster); + PotentialCluster->Neighbor = + FindNearestNeighbor (Tree, PotentialCluster->Cluster, + &(HeapEntry.Key)); + if (PotentialCluster->Neighbor != NULL) { + HeapStore(Heap, &HeapEntry); + } + } + } + + // the root node in the cluster tree is now the only node in the kd-tree + Clusterer->Root = (CLUSTER *) RootOf (Clusterer->KDTree); + + // free up the memory used by the K-D tree, heap, and temp clusters + FreeKDTree(Tree); + Clusterer->KDTree = NULL; + FreeHeap(Heap); + memfree(TempCluster); +} // CreateClusterTree + + +/** MakePotentialClusters ************************************************** +Parameters: Cluster current cluster being visited in kd-tree walk + Order order in which cluster is being visited + Level level of this cluster in the kd-tree +Globals: Tree kd-tree to be searched for neighbors + TempCluster array of temporary clusters + CurrentTemp index of next temp cluster to be used + Heap heap used to hold temp clusters - "best" on top +Operation: This routine is designed to be used in concert with the + KDWalk routine. It will create a potential cluster for + each sample in the kd-tree that is being walked. This + potential cluster will then be pushed on the heap. +Return: none +Exceptions: none +History: 5/29/89, DSJ, Created. + 7/13/89, DSJ, Removed visibility of kd-tree node data struct. +******************************************************************************/ +void MakePotentialClusters(CLUSTER *Cluster, VISIT Order, INT32 Level) { + HEAPENTRY HeapEntry; + + if ((Order == preorder) || (Order == leaf)) { + TempCluster[CurrentTemp].Cluster = Cluster; + HeapEntry.Data = (char *) &(TempCluster[CurrentTemp]); + TempCluster[CurrentTemp].Neighbor = + FindNearestNeighbor (Tree, TempCluster[CurrentTemp].Cluster, + &(HeapEntry.Key)); + if (TempCluster[CurrentTemp].Neighbor != NULL) { + HeapStore(Heap, &HeapEntry); + CurrentTemp++; + } + } +} // MakePotentialClusters + + +/** FindNearestNeighbor ********************************************************* +Parameters: Tree kd-tree to search in for nearest neighbor + Cluster cluster whose nearest neighbor is to be found + Distance ptr to variable to report distance found +Globals: none +Operation: This routine searches the specified kd-tree for the nearest + neighbor of the specified cluster. It actually uses the + kd routines to find the 2 nearest neighbors since one of them + will be the original cluster. A pointer to the nearest + neighbor is returned, if it can be found, otherwise NULL is + returned. The distance between the 2 nodes is placed + in the specified variable. +Return: Pointer to the nearest neighbor of Cluster, or NULL +Exceptions: none +History: 5/29/89, DSJ, Created. + 7/13/89, DSJ, Removed visibility of kd-tree node data struct +********************************************************************************/ +CLUSTER * +FindNearestNeighbor (KDTREE * Tree, CLUSTER * Cluster, FLOAT32 * Distance) +#define MAXNEIGHBORS 2 +#define MAXDISTANCE MAX_FLOAT32 +{ + CLUSTER *Neighbor[MAXNEIGHBORS]; + FLOAT32 Dist[MAXNEIGHBORS]; + INT32 NumberOfNeighbors; + INT32 i; + CLUSTER *BestNeighbor; + + // find the 2 nearest neighbors of the cluster + NumberOfNeighbors = KDNearestNeighborSearch + (Tree, Cluster->Mean, MAXNEIGHBORS, MAXDISTANCE, Neighbor, Dist); + + // search for the nearest neighbor that is not the cluster itself + *Distance = MAXDISTANCE; + BestNeighbor = NULL; + for (i = 0; i < NumberOfNeighbors; i++) { + if ((Dist[i] < *Distance) && (Neighbor[i] != Cluster)) { + *Distance = Dist[i]; + BestNeighbor = Neighbor[i]; + } + } + return (BestNeighbor); +} // FindNearestNeighbor + + +/** MakeNewCluster ************************************************************* +Parameters: Clusterer current clustering environment + TempCluster potential cluster to make permanent +Globals: none +Operation: This routine creates a new permanent cluster from the + clusters specified in TempCluster. The 2 clusters in + TempCluster are marked as "clustered" and deleted from + the kd-tree. The new cluster is then added to the kd-tree. + Return: Pointer to the new permanent cluster +Exceptions: none +History: 5/29/89, DSJ, Created. + 7/13/89, DSJ, Removed visibility of kd-tree node data struct +********************************************************************************/ +CLUSTER *MakeNewCluster(CLUSTERER *Clusterer, TEMPCLUSTER *TempCluster) { + CLUSTER *Cluster; + + // allocate the new cluster and initialize it + Cluster = (CLUSTER *) Emalloc (sizeof (CLUSTER) + + (Clusterer->SampleSize - + 1) * sizeof (FLOAT32)); + Cluster->Clustered = FALSE; + Cluster->Prototype = FALSE; + Cluster->Left = TempCluster->Cluster; + Cluster->Right = TempCluster->Neighbor; + Cluster->CharID = -1; + + // mark the old clusters as "clustered" and delete them from the kd-tree + Cluster->Left->Clustered = TRUE; + Cluster->Right->Clustered = TRUE; + KDDelete (Clusterer->KDTree, Cluster->Left->Mean, Cluster->Left); + KDDelete (Clusterer->KDTree, Cluster->Right->Mean, Cluster->Right); + + // compute the mean and sample count for the new cluster + Cluster->SampleCount = + MergeClusters (Clusterer->SampleSize, Clusterer->ParamDesc, + Cluster->Left->SampleCount, Cluster->Right->SampleCount, + Cluster->Mean, Cluster->Left->Mean, Cluster->Right->Mean); + + // add the new cluster to the KD tree + KDStore (Clusterer->KDTree, Cluster->Mean, Cluster); + return (Cluster); +} // MakeNewCluster + + +/** MergeClusters ************************************************************ +Parameters: N # of dimensions (size of arrays) + ParamDesc array of dimension descriptions + n1, n2 number of samples in each old cluster + m array to hold mean of new cluster + m1, m2 arrays containing means of old clusters +Globals: None +Operation: This routine merges two clusters into one larger cluster. + To do this it computes the number of samples in the new + cluster and the mean of the new cluster. The ParamDesc + information is used to ensure that circular dimensions + are handled correctly. +Return: The number of samples in the new cluster. +Exceptions: None +History: 5/31/89, DSJ, Created. +*********************************************************************************/ +INT32 +MergeClusters (INT16 N, +register PARAM_DESC ParamDesc[], +register INT32 n1, +register INT32 n2, +register FLOAT32 m[], +register FLOAT32 m1[], register FLOAT32 m2[]) { + register INT32 i, n; + + n = n1 + n2; + for (i = N; i > 0; i--, ParamDesc++, m++, m1++, m2++) { + if (ParamDesc->Circular) { + // if distance between means is greater than allowed + // reduce upper point by one "rotation" to compute mean + // then normalize the mean back into the accepted range + if ((*m2 - *m1) > ParamDesc->HalfRange) { + *m = (n1 * *m1 + n2 * (*m2 - ParamDesc->Range)) / n; + if (*m < ParamDesc->Min) + *m += ParamDesc->Range; + } + else if ((*m1 - *m2) > ParamDesc->HalfRange) { + *m = (n1 * (*m1 - ParamDesc->Range) + n2 * *m2) / n; + if (*m < ParamDesc->Min) + *m += ParamDesc->Range; + } + else + *m = (n1 * *m1 + n2 * *m2) / n; + } + else + *m = (n1 * *m1 + n2 * *m2) / n; + } + return (n); +} // MergeClusters + + +/** ComputePrototypes ******************************************************* +Parameters: Clusterer data structure holding cluster tree + Config parameters used to control prototype generation +Globals: None +Operation: This routine decides which clusters in the cluster tree + should be represented by prototypes, forms a list of these + prototypes, and places the list in the Clusterer data + structure. +Return: None +Exceptions: None +History: 5/30/89, DSJ, Created. +*******************************************************************************/ +void ComputePrototypes(CLUSTERER *Clusterer, CLUSTERCONFIG *Config) { + LIST ClusterStack = NIL; + CLUSTER *Cluster; + PROTOTYPE *Prototype; + + // use a stack to keep track of clusters waiting to be processed + // initially the only cluster on the stack is the root cluster + if (Clusterer->Root != NULL) + ClusterStack = push (NIL, Clusterer->Root); + + // loop until we have analyzed all clusters which are potential prototypes + while (ClusterStack != NIL) { + // remove the next cluster to be analyzed from the stack + // try to make a prototype from the cluster + // if successful, put it on the proto list, else split the cluster + Cluster = (CLUSTER *) first (ClusterStack); + ClusterStack = pop (ClusterStack); + Prototype = MakePrototype (Clusterer, Config, Cluster); + if (Prototype != NULL) { + Clusterer->ProtoList = push (Clusterer->ProtoList, Prototype); + } + else { + ClusterStack = push (ClusterStack, Cluster->Right); + ClusterStack = push (ClusterStack, Cluster->Left); + } + } +} // ComputePrototypes + + +/** MakePrototype *********************************************************** +Parameters: Clusterer data structure holding cluster tree + Config parameters used to control prototype generation + Cluster cluster to be made into a prototype +Globals: None +Operation: This routine attempts to create a prototype from the + specified cluster that conforms to the distribution + specified in Config. If there are too few samples in the + cluster to perform a statistical analysis, then a prototype + is generated but labelled as insignificant. If the + dimensions of the cluster are not independent, no prototype + is generated and NULL is returned. If a prototype can be + found that matches the desired distribution then a pointer + to it is returned, otherwise NULL is returned. +Return: Pointer to new prototype or NULL +Exceptions: None +History: 6/19/89, DSJ, Created. +*******************************************************************************/ +PROTOTYPE *MakePrototype(CLUSTERER *Clusterer, + CLUSTERCONFIG *Config, + CLUSTER *Cluster) { + STATISTICS *Statistics; + PROTOTYPE *Proto; + BUCKETS *Buckets; + + // filter out clusters which contain samples from the same character + if (MultipleCharSamples (Clusterer, Cluster, Config->MaxIllegal)) + return (NULL); + + // compute the covariance matrix and ranges for the cluster + Statistics = + ComputeStatistics (Clusterer->SampleSize, Clusterer->ParamDesc, Cluster); + + // check for degenerate clusters which need not be analyzed further + // note that the MinSamples test assumes that all clusters with multiple + // character samples have been removed (as above) + Proto = MakeDegenerateProto (Clusterer->SampleSize, Cluster, Statistics, + Config->ProtoStyle, + (INT32) (Config->MinSamples * + Clusterer->NumChar)); + if (Proto != NULL) { + FreeStatistics(Statistics); + return (Proto); + } + // check to ensure that all dimensions are independent + if (!Independent (Clusterer->ParamDesc, Clusterer->SampleSize, + Statistics->CoVariance, Config->Independence)) { + FreeStatistics(Statistics); + return (NULL); + } + + if (HOTELLING && Config->ProtoStyle == elliptical) { + Proto = TestEllipticalProto(Clusterer, Cluster, Statistics); + if (Proto != NULL) { + FreeStatistics(Statistics); + return Proto; + } + } + + // create a histogram data structure used to evaluate distributions + Buckets = GetBuckets (normal, Cluster->SampleCount, Config->Confidence); + + // create a prototype based on the statistics and test it + switch (Config->ProtoStyle) { + case spherical: + Proto = MakeSphericalProto (Clusterer, Cluster, Statistics, Buckets); + break; + case elliptical: + Proto = MakeEllipticalProto (Clusterer, Cluster, Statistics, Buckets); + break; + case mixed: + Proto = MakeMixedProto (Clusterer, Cluster, Statistics, Buckets, + Config->Confidence); + break; + case automatic: + Proto = MakeSphericalProto (Clusterer, Cluster, Statistics, Buckets); + if (Proto != NULL) + break; + Proto = MakeEllipticalProto (Clusterer, Cluster, Statistics, Buckets); + if (Proto != NULL) + break; + Proto = MakeMixedProto (Clusterer, Cluster, Statistics, Buckets, + Config->Confidence); + break; + } + FreeBuckets(Buckets); + FreeStatistics(Statistics); + return (Proto); +} // MakePrototype + + +/** MakeDegenerateProto ****************************************************** +Parameters: N number of dimensions + Cluster cluster being analyzed + Statistics statistical info about cluster + Style type of prototype to be generated + MinSamples minimum number of samples in a cluster +Globals: None +Operation: This routine checks for clusters which are degenerate and + therefore cannot be analyzed in a statistically valid way. + A cluster is defined as degenerate if it does not have at + least MINSAMPLESNEEDED samples in it. If the cluster is + found to be degenerate, a prototype of the specified style + is generated and marked as insignificant. A cluster is + also degenerate if it does not have at least MinSamples + samples in it. + If the cluster is not degenerate, NULL is returned. +Return: Pointer to degenerate prototype or NULL. +Exceptions: None +History: 6/20/89, DSJ, Created. + 7/12/89, DSJ, Changed name and added check for 0 stddev. + 8/8/89, DSJ, Removed check for 0 stddev (handled elsewhere). +********************************************************************************/ +PROTOTYPE *MakeDegenerateProto( //this was MinSample + UINT16 N, + CLUSTER *Cluster, + STATISTICS *Statistics, + PROTOSTYLE Style, + INT32 MinSamples) { + PROTOTYPE *Proto = NULL; + + if (MinSamples < MINSAMPLESNEEDED) + MinSamples = MINSAMPLESNEEDED; + + if (Cluster->SampleCount < MinSamples) { + switch (Style) { + case spherical: + Proto = NewSphericalProto (N, Cluster, Statistics); + break; + case elliptical: + case automatic: + Proto = NewEllipticalProto (N, Cluster, Statistics); + break; + case mixed: + Proto = NewMixedProto (N, Cluster, Statistics); + break; + } + Proto->Significant = FALSE; + } + return (Proto); +} // MakeDegenerateProto + +/** TestEllipticalProto **************************************************** +Parameters: Clusterer data struct containing samples being clustered + Cluster cluster to be made into an elliptical prototype + Statistics statistical info about cluster +Globals: None +Operation: This routine tests the specified cluster to see if ** +* there is a statistically significant difference between +* the sub-clusters that would be made if the cluster were to +* be split. If not, then a new prototype is formed and +* returned to the caller. If there is, then NULL is returned +* to the caller. +Return: Pointer to new elliptical prototype or NULL. +****************************************************************************/ +PROTOTYPE *TestEllipticalProto(CLUSTERER *Clusterer, + CLUSTER *Cluster, + STATISTICS *Statistics) { + int N = Clusterer->SampleSize; + CLUSTER* Left = Cluster->Left; + CLUSTER* Right = Cluster->Right; + if (Left == NULL || Right == NULL) + return NULL; + int TotalDims = Left->SampleCount + Right->SampleCount; + if (TotalDims < N + 1) + return NULL; + FLOAT32* Inverse = (FLOAT32 *) Emalloc(N * N * sizeof(FLOAT32)); + FLOAT32* Delta = (FLOAT32*) Emalloc(N * sizeof(FLOAT32)); + double err = InvertMatrix(Statistics->CoVariance, N, Inverse); + if (err > 1) { + cprintf("Clustering error: Matrix inverse failed with error %g\n", err); + } + for (int dim = 0; dim < N; ++dim) { + Delta[dim] = Left->Mean[dim] - Right->Mean[dim]; + } + // Compute Hotelling's T-squared. + double Tsq = 0.0; + for (int x = 0; x < N; ++x) { + double temp = 0.0; + for (int y = 0; y < N; ++y) { + temp += Inverse[y + N*x] * Delta[y]; + } + Tsq += Delta[x] * temp; + } + memfree(Inverse); + memfree(Delta); + Tsq *= Left->SampleCount * Right->SampleCount / TotalDims; + double F = Tsq * (TotalDims - N - 1) / ((TotalDims - N) * 2); + int Fx = N; + if (Fx > FTABLE_X) + Fx = FTABLE_X; + --Fx; + int Fy = TotalDims - N - 1; + if (Fy > FTABLE_Y) + Fy = FTABLE_Y; + --Fy; + if (F < FTable[Fy][Fx]) { + return NewEllipticalProto (Clusterer->SampleSize, Cluster, Statistics); + } + return NULL; +} + +/* MakeSphericalProto ******************************************************* +Parameters: Clusterer data struct containing samples being clustered + Cluster cluster to be made into a spherical prototype + Statistics statistical info about cluster + Buckets histogram struct used to analyze distribution +Globals: None +Operation: This routine tests the specified cluster to see if it can + be approximated by a spherical normal distribution. If it + can be, then a new prototype is formed and returned to the + caller. If it can't be, then NULL is returned to the caller. +Return: Pointer to new spherical prototype or NULL. +Exceptions: None +History: 6/1/89, DSJ, Created. +******************************************************************************/ +PROTOTYPE *MakeSphericalProto(CLUSTERER *Clusterer, + CLUSTER *Cluster, + STATISTICS *Statistics, + BUCKETS *Buckets) { + PROTOTYPE *Proto = NULL; + int i; + + // check that each dimension is a normal distribution + for (i = 0; i < Clusterer->SampleSize; i++) { + if (Clusterer->ParamDesc[i].NonEssential) + continue; + + FillBuckets (Buckets, Cluster, i, &(Clusterer->ParamDesc[i]), + Cluster->Mean[i], + sqrt ((FLOAT64) (Statistics->AvgVariance))); + if (!DistributionOK (Buckets)) + break; + } + // if all dimensions matched a normal distribution, make a proto + if (i >= Clusterer->SampleSize) + Proto = NewSphericalProto (Clusterer->SampleSize, Cluster, Statistics); + return (Proto); +} // MakeSphericalProto + + +/** MakeEllipticalProto **************************************************** +Parameters: Clusterer data struct containing samples being clustered + Cluster cluster to be made into an elliptical prototype + Statistics statistical info about cluster + Buckets histogram struct used to analyze distribution +Globals: None +Operation: This routine tests the specified cluster to see if it can + be approximated by an elliptical normal distribution. If it + can be, then a new prototype is formed and returned to the + caller. If it can't be, then NULL is returned to the caller. +Return: Pointer to new elliptical prototype or NULL. +Exceptions: None +History: 6/12/89, DSJ, Created. +****************************************************************************/ +PROTOTYPE *MakeEllipticalProto(CLUSTERER *Clusterer, + CLUSTER *Cluster, + STATISTICS *Statistics, + BUCKETS *Buckets) { + PROTOTYPE *Proto = NULL; + int i; + + // check that each dimension is a normal distribution + for (i = 0; i < Clusterer->SampleSize; i++) { + if (Clusterer->ParamDesc[i].NonEssential) + continue; + + FillBuckets (Buckets, Cluster, i, &(Clusterer->ParamDesc[i]), + Cluster->Mean[i], + sqrt ((FLOAT64) Statistics-> + CoVariance[i * (Clusterer->SampleSize + 1)])); + if (!DistributionOK (Buckets)) + break; + } + // if all dimensions matched a normal distribution, make a proto + if (i >= Clusterer->SampleSize) + Proto = NewEllipticalProto (Clusterer->SampleSize, Cluster, Statistics); + return (Proto); +} // MakeEllipticalProto + + +/** MakeMixedProto *********************************************************** +Parameters: Clusterer data struct containing samples being clustered + Cluster cluster to be made into a prototype + Statistics statistical info about cluster + NormalBuckets histogram struct used to analyze distribution + Confidence confidence level for alternate distributions +Globals: None +Operation: This routine tests each dimension of the specified cluster to + see what distribution would best approximate that dimension. + Each dimension is compared to the following distributions + in order: normal, random, uniform. If each dimension can + be represented by one of these distributions, + then a new prototype is formed and returned to the + caller. If it can't be, then NULL is returned to the caller. +Return: Pointer to new mixed prototype or NULL. +Exceptions: None +History: 6/12/89, DSJ, Created. +********************************************************************************/ +PROTOTYPE *MakeMixedProto(CLUSTERER *Clusterer, + CLUSTER *Cluster, + STATISTICS *Statistics, + BUCKETS *NormalBuckets, + FLOAT64 Confidence) { + PROTOTYPE *Proto; + int i; + BUCKETS *UniformBuckets = NULL; + BUCKETS *RandomBuckets = NULL; + + // create a mixed proto to work on - initially assume all dimensions normal*/ + Proto = NewMixedProto (Clusterer->SampleSize, Cluster, Statistics); + + // find the proper distribution for each dimension + for (i = 0; i < Clusterer->SampleSize; i++) { + if (Clusterer->ParamDesc[i].NonEssential) + continue; + + FillBuckets (NormalBuckets, Cluster, i, &(Clusterer->ParamDesc[i]), + Proto->Mean[i], + sqrt ((FLOAT64) Proto->Variance.Elliptical[i])); + if (DistributionOK (NormalBuckets)) + continue; + + if (RandomBuckets == NULL) + RandomBuckets = + GetBuckets (D_random, Cluster->SampleCount, Confidence); + MakeDimRandom (i, Proto, &(Clusterer->ParamDesc[i])); + FillBuckets (RandomBuckets, Cluster, i, &(Clusterer->ParamDesc[i]), + Proto->Mean[i], Proto->Variance.Elliptical[i]); + if (DistributionOK (RandomBuckets)) + continue; + + if (UniformBuckets == NULL) + UniformBuckets = + GetBuckets (uniform, Cluster->SampleCount, Confidence); + MakeDimUniform(i, Proto, Statistics); + FillBuckets (UniformBuckets, Cluster, i, &(Clusterer->ParamDesc[i]), + Proto->Mean[i], Proto->Variance.Elliptical[i]); + if (DistributionOK (UniformBuckets)) + continue; + break; + } + // if any dimension failed to match a distribution, discard the proto + if (i < Clusterer->SampleSize) { + FreePrototype(Proto); + Proto = NULL; + } + if (UniformBuckets != NULL) + FreeBuckets(UniformBuckets); + if (RandomBuckets != NULL) + FreeBuckets(RandomBuckets); + return (Proto); +} // MakeMixedProto + + +/* MakeDimRandom ************************************************************* +Parameters: i index of dimension to be changed + Proto prototype whose dimension is to be altered + ParamDesc description of specified dimension +Globals: None +Operation: This routine alters the ith dimension of the specified + mixed prototype to be D_random. +Return: None +Exceptions: None +History: 6/20/89, DSJ, Created. +******************************************************************************/ +void MakeDimRandom(UINT16 i, PROTOTYPE *Proto, PARAM_DESC *ParamDesc) { + Proto->Distrib[i] = D_random; + Proto->Mean[i] = ParamDesc->MidRange; + Proto->Variance.Elliptical[i] = ParamDesc->HalfRange; + + // subtract out the previous magnitude of this dimension from the total + Proto->TotalMagnitude /= Proto->Magnitude.Elliptical[i]; + Proto->Magnitude.Elliptical[i] = 1.0 / ParamDesc->Range; + Proto->TotalMagnitude *= Proto->Magnitude.Elliptical[i]; + Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); + + // note that the proto Weight is irrelevant for D_random protos +} // MakeDimRandom + + +/** MakeDimUniform *********************************************************** +Parameters: i index of dimension to be changed + Proto prototype whose dimension is to be altered + Statistics statistical info about prototype +Globals: None +Operation: This routine alters the ith dimension of the specified + mixed prototype to be uniform. +Return: None +Exceptions: None +History: 6/20/89, DSJ, Created. +******************************************************************************/ +void MakeDimUniform(UINT16 i, PROTOTYPE *Proto, STATISTICS *Statistics) { + Proto->Distrib[i] = uniform; + Proto->Mean[i] = Proto->Cluster->Mean[i] + + (Statistics->Min[i] + Statistics->Max[i]) / 2; + Proto->Variance.Elliptical[i] = + (Statistics->Max[i] - Statistics->Min[i]) / 2; + if (Proto->Variance.Elliptical[i] < MINVARIANCE) + Proto->Variance.Elliptical[i] = MINVARIANCE; + + // subtract out the previous magnitude of this dimension from the total + Proto->TotalMagnitude /= Proto->Magnitude.Elliptical[i]; + Proto->Magnitude.Elliptical[i] = + 1.0 / (2.0 * Proto->Variance.Elliptical[i]); + Proto->TotalMagnitude *= Proto->Magnitude.Elliptical[i]; + Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); + + // note that the proto Weight is irrelevant for uniform protos +} // MakeDimUniform + + +/** ComputeStatistics ********************************************************* +Parameters: N number of dimensions + ParamDesc array of dimension descriptions + Cluster cluster whose stats are to be computed +Globals: None +Operation: This routine searches the cluster tree for all leaf nodes + which are samples in the specified cluster. It computes + a full covariance matrix for these samples as well as + keeping track of the ranges (min and max) for each + dimension. A special data structure is allocated to + return this information to the caller. An incremental + algorithm for computing statistics is not used because + it will not work with circular dimensions. +Return: Pointer to new data structure containing statistics +Exceptions: None +History: 6/2/89, DSJ, Created. +*********************************************************************************/ +STATISTICS * +ComputeStatistics (INT16 N, PARAM_DESC ParamDesc[], CLUSTER * Cluster) { + STATISTICS *Statistics; + int i, j; + FLOAT32 *CoVariance; + FLOAT32 *Distance; + LIST SearchState; + SAMPLE *Sample; + UINT32 SampleCountAdjustedForBias; + + // allocate memory to hold the statistics results + Statistics = (STATISTICS *) Emalloc (sizeof (STATISTICS)); + Statistics->CoVariance = (FLOAT32 *) Emalloc (N * N * sizeof (FLOAT32)); + Statistics->Min = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + Statistics->Max = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + + // allocate temporary memory to hold the sample to mean distances + Distance = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + + // initialize the statistics + Statistics->AvgVariance = 1.0; + CoVariance = Statistics->CoVariance; + for (i = 0; i < N; i++) { + Statistics->Min[i] = 0.0; + Statistics->Max[i] = 0.0; + for (j = 0; j < N; j++, CoVariance++) + *CoVariance = 0; + } + // find each sample in the cluster and merge it into the statistics + InitSampleSearch(SearchState, Cluster); + while ((Sample = NextSample (&SearchState)) != NULL) { + for (i = 0; i < N; i++) { + Distance[i] = Sample->Mean[i] - Cluster->Mean[i]; + if (ParamDesc[i].Circular) { + if (Distance[i] > ParamDesc[i].HalfRange) + Distance[i] -= ParamDesc[i].Range; + if (Distance[i] < -ParamDesc[i].HalfRange) + Distance[i] += ParamDesc[i].Range; + } + if (Distance[i] < Statistics->Min[i]) + Statistics->Min[i] = Distance[i]; + if (Distance[i] > Statistics->Max[i]) + Statistics->Max[i] = Distance[i]; + } + CoVariance = Statistics->CoVariance; + for (i = 0; i < N; i++) + for (j = 0; j < N; j++, CoVariance++) + *CoVariance += Distance[i] * Distance[j]; + } + // normalize the variances by the total number of samples + // use SampleCount-1 instead of SampleCount to get an unbiased estimate + // also compute the geometic mean of the diagonal variances + // ensure that clusters with only 1 sample are handled correctly + if (Cluster->SampleCount > 1) + SampleCountAdjustedForBias = Cluster->SampleCount - 1; + else + SampleCountAdjustedForBias = 1; + CoVariance = Statistics->CoVariance; + for (i = 0; i < N; i++) + for (j = 0; j < N; j++, CoVariance++) { + *CoVariance /= SampleCountAdjustedForBias; + if (j == i) { + if (*CoVariance < MINVARIANCE) + *CoVariance = MINVARIANCE; + Statistics->AvgVariance *= *CoVariance; + } + } + Statistics->AvgVariance = (float)pow((double)Statistics->AvgVariance, + 1.0 / N); + + // release temporary memory and return + memfree(Distance); + return (Statistics); +} // ComputeStatistics + + +/** NewSpericalProto ********************************************************* +Parameters: N number of dimensions + Cluster cluster to be made into a spherical prototype + Statistics statistical info about samples in cluster +Globals: None +Operation: This routine creates a spherical prototype data structure to + approximate the samples in the specified cluster. + Spherical prototypes have a single variance which is + common across all dimensions. All dimensions are normally + distributed and independent. +Return: Pointer to a new spherical prototype data structure +Exceptions: None +History: 6/19/89, DSJ, Created. +******************************************************************************/ +PROTOTYPE *NewSphericalProto(UINT16 N, + CLUSTER *Cluster, + STATISTICS *Statistics) { + PROTOTYPE *Proto; + + Proto = NewSimpleProto (N, Cluster); + + Proto->Variance.Spherical = Statistics->AvgVariance; + if (Proto->Variance.Spherical < MINVARIANCE) + Proto->Variance.Spherical = MINVARIANCE; + + Proto->Magnitude.Spherical = + 1.0 / sqrt ((double) (2.0 * PI * Proto->Variance.Spherical)); + Proto->TotalMagnitude = (float)pow((double)Proto->Magnitude.Spherical, + (double) N); + Proto->Weight.Spherical = 1.0 / Proto->Variance.Spherical; + Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); + + return (Proto); +} // NewSphericalProto + + +/** NewEllipticalProto ******************************************************* +Parameters: N number of dimensions + Cluster cluster to be made into an elliptical prototype + Statistics statistical info about samples in cluster +Globals: None +Operation: This routine creates an elliptical prototype data structure to + approximate the samples in the specified cluster. + Elliptical prototypes have a variance for each dimension. + All dimensions are normally distributed and independent. +Return: Pointer to a new elliptical prototype data structure +Exceptions: None +History: 6/19/89, DSJ, Created. +*******************************************************************************/ +PROTOTYPE *NewEllipticalProto(INT16 N, + CLUSTER *Cluster, + STATISTICS *Statistics) { + PROTOTYPE *Proto; + FLOAT32 *CoVariance; + int i; + + Proto = NewSimpleProto (N, Cluster); + Proto->Variance.Elliptical = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + Proto->Magnitude.Elliptical = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + Proto->Weight.Elliptical = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + + CoVariance = Statistics->CoVariance; + Proto->TotalMagnitude = 1.0; + for (i = 0; i < N; i++, CoVariance += N + 1) { + Proto->Variance.Elliptical[i] = *CoVariance; + if (Proto->Variance.Elliptical[i] < MINVARIANCE) + Proto->Variance.Elliptical[i] = MINVARIANCE; + + Proto->Magnitude.Elliptical[i] = + 1.0 / sqrt ((double) (2.0 * PI * Proto->Variance.Elliptical[i])); + Proto->Weight.Elliptical[i] = 1.0 / Proto->Variance.Elliptical[i]; + Proto->TotalMagnitude *= Proto->Magnitude.Elliptical[i]; + } + Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); + Proto->Style = elliptical; + return (Proto); +} // NewEllipticalProto + + +/** MewMixedProto ************************************************************ +Parameters: N number of dimensions + Cluster cluster to be made into a mixed prototype + Statistics statistical info about samples in cluster +Globals: None +Operation: This routine creates a mixed prototype data structure to + approximate the samples in the specified cluster. + Mixed prototypes can have different distributions for + each dimension. All dimensions are independent. The + structure is initially filled in as though it were an + elliptical prototype. The actual distributions of the + dimensions can be altered by other routines. +Return: Pointer to a new mixed prototype data structure +Exceptions: None +History: 6/19/89, DSJ, Created. +********************************************************************************/ +PROTOTYPE *NewMixedProto(INT16 N, CLUSTER *Cluster, STATISTICS *Statistics) { + PROTOTYPE *Proto; + int i; + + Proto = NewEllipticalProto (N, Cluster, Statistics); + Proto->Distrib = (DISTRIBUTION *) Emalloc (N * sizeof (DISTRIBUTION)); + + for (i = 0; i < N; i++) { + Proto->Distrib[i] = normal; + } + Proto->Style = mixed; + return (Proto); +} // NewMixedProto + + +/** NewSimpleProto *********************************************************** +Parameters: N number of dimensions + Cluster cluster to be made into a prototype +Globals: None +Operation: This routine allocates memory to hold a simple prototype + data structure, i.e. one without independent distributions + and variances for each dimension. +Return: Pointer to new simple prototype +Exceptions: None +History: 6/19/89, DSJ, Created. +*******************************************************************************/ +PROTOTYPE *NewSimpleProto(INT16 N, CLUSTER *Cluster) { + PROTOTYPE *Proto; + int i; + + Proto = (PROTOTYPE *) Emalloc (sizeof (PROTOTYPE)); + Proto->Mean = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + + for (i = 0; i < N; i++) + Proto->Mean[i] = Cluster->Mean[i]; + Proto->Distrib = NULL; + + Proto->Significant = TRUE; + Proto->Style = spherical; + Proto->NumSamples = Cluster->SampleCount; + Proto->Cluster = Cluster; + Proto->Cluster->Prototype = TRUE; + return (Proto); +} // NewSimpleProto + + +/** Independent *************************************************************** +Parameters: ParamDesc descriptions of each feature space dimension + N number of dimensions + CoVariance ptr to a covariance matrix + Independence max off-diagonal correlation coefficient +Globals: None +Operation: This routine returns TRUE if the specified covariance + matrix indicates that all N dimensions are independent of + one another. One dimension is judged to be independent of + another when the magnitude of the corresponding correlation + coefficient is + less than the specified Independence factor. The + correlation coefficient is calculated as: (see Duda and + Hart, pg. 247) + coeff[ij] = stddev[ij] / sqrt (stddev[ii] * stddev[jj]) + The covariance matrix is assumed to be symmetric (which + should always be true). +Return: TRUE if dimensions are independent, FALSE otherwise +Exceptions: None +History: 6/4/89, DSJ, Created. +*******************************************************************************/ +BOOL8 +Independent (PARAM_DESC ParamDesc[], +INT16 N, FLOAT32 * CoVariance, FLOAT32 Independence) { + int i, j; + FLOAT32 *VARii; // points to ith on-diagonal element + FLOAT32 *VARjj; // points to jth on-diagonal element + FLOAT32 CorrelationCoeff; + + VARii = CoVariance; + for (i = 0; i < N; i++, VARii += N + 1) { + if (ParamDesc[i].NonEssential) + continue; + + VARjj = VARii + N + 1; + CoVariance = VARii + 1; + for (j = i + 1; j < N; j++, CoVariance++, VARjj += N + 1) { + if (ParamDesc[j].NonEssential) + continue; + + if ((*VARii == 0.0) || (*VARjj == 0.0)) + CorrelationCoeff = 0.0; + else + CorrelationCoeff = + sqrt (sqrt (*CoVariance * *CoVariance / (*VARii * *VARjj))); + if (CorrelationCoeff > Independence) + return (FALSE); + } + } + return (TRUE); +} // Independent + + +/** GetBuckets ************************************************************** +Parameters: Distribution type of probability distribution to test for + SampleCount number of samples that are available + Confidence probability of a Type I error +Globals: none +Operation: This routine returns a histogram data structure which can + be used by other routines to place samples into histogram + buckets, and then apply a goodness of fit test to the + histogram data to determine if the samples belong to the + specified probability distribution. The routine keeps + a list of bucket data structures which have already been + created so that it minimizes the computation time needed + to create a new bucket. +Return: Bucket data structure +Exceptions: none +History: Thu Aug 3 12:58:10 1989, DSJ, Created. +*****************************************************************************/ +BUCKETS *GetBuckets(DISTRIBUTION Distribution, + UINT32 SampleCount, + FLOAT64 Confidence) { + UINT16 NumberOfBuckets; + BUCKETS *Buckets; + + // search for an old bucket structure with the same number of buckets + NumberOfBuckets = OptimumNumberOfBuckets (SampleCount); + Buckets = (BUCKETS *) first (search (OldBuckets[(int) Distribution], + &NumberOfBuckets, NumBucketsMatch)); + + // if a matching bucket structure is found, delete it from the list + if (Buckets != NULL) { + OldBuckets[(int) Distribution] = + delete_d (OldBuckets[(int) Distribution], Buckets, ListEntryMatch); + if (SampleCount != Buckets->SampleCount) + AdjustBuckets(Buckets, SampleCount); + if (Confidence != Buckets->Confidence) { + Buckets->Confidence = Confidence; + Buckets->ChiSquared = ComputeChiSquared + (DegreesOfFreedom (Distribution, Buckets->NumberOfBuckets), + Confidence); + } + InitBuckets(Buckets); + } + else // otherwise create a new structure + Buckets = MakeBuckets (Distribution, SampleCount, Confidence); + return (Buckets); +} // GetBuckets + + +/** Makebuckets ************************************************************* +Parameters: Distribution type of probability distribution to test for + SampleCount number of samples that are available + Confidence probability of a Type I error +Globals: None +Operation: This routine creates a histogram data structure which can + be used by other routines to place samples into histogram + buckets, and then apply a goodness of fit test to the + histogram data to determine if the samples belong to the + specified probability distribution. The buckets are + allocated in such a way that the expected frequency of + samples in each bucket is approximately the same. In + order to make this possible, a mapping table is + computed which maps "normalized" samples into the + appropriate bucket. +Return: Pointer to new histogram data structure +Exceptions: None +History: 6/4/89, DSJ, Created. +*****************************************************************************/ +BUCKETS *MakeBuckets(DISTRIBUTION Distribution, + UINT32 SampleCount, + FLOAT64 Confidence) { + static DENSITYFUNC DensityFunction[] = + { NormalDensity, UniformDensity, UniformDensity }; + int i, j; + BUCKETS *Buckets; + FLOAT64 BucketProbability; + FLOAT64 NextBucketBoundary; + FLOAT64 Probability; + FLOAT64 ProbabilityDelta; + FLOAT64 LastProbDensity; + FLOAT64 ProbDensity; + UINT16 CurrentBucket; + BOOL8 Symmetrical; + + // allocate memory needed for data structure + Buckets = (BUCKETS *) Emalloc (sizeof (BUCKETS)); + Buckets->NumberOfBuckets = OptimumNumberOfBuckets (SampleCount); + Buckets->SampleCount = SampleCount; + Buckets->Confidence = Confidence; + Buckets->Count = + (UINT32 *) Emalloc (Buckets->NumberOfBuckets * sizeof (UINT32)); + Buckets->ExpectedCount = + (FLOAT32 *) Emalloc (Buckets->NumberOfBuckets * sizeof (FLOAT32)); + + // initialize simple fields + Buckets->Distribution = Distribution; + for (i = 0; i < Buckets->NumberOfBuckets; i++) { + Buckets->Count[i] = 0; + Buckets->ExpectedCount[i] = 0.0; + } + + // all currently defined distributions are symmetrical + Symmetrical = TRUE; + Buckets->ChiSquared = ComputeChiSquared + (DegreesOfFreedom (Distribution, Buckets->NumberOfBuckets), Confidence); + + if (Symmetrical) { + // allocate buckets so that all have approx. equal probability + BucketProbability = 1.0 / (FLOAT64) (Buckets->NumberOfBuckets); + + // distribution is symmetric so fill in upper half then copy + CurrentBucket = Buckets->NumberOfBuckets / 2; + if (Odd (Buckets->NumberOfBuckets)) + NextBucketBoundary = BucketProbability / 2; + else + NextBucketBoundary = BucketProbability; + + Probability = 0.0; + LastProbDensity = + (*DensityFunction[(int) Distribution]) (BUCKETTABLESIZE / 2); + for (i = BUCKETTABLESIZE / 2; i < BUCKETTABLESIZE; i++) { + ProbDensity = (*DensityFunction[(int) Distribution]) (i + 1); + ProbabilityDelta = Integral (LastProbDensity, ProbDensity, 1.0); + Probability += ProbabilityDelta; + if (Probability > NextBucketBoundary) { + if (CurrentBucket < Buckets->NumberOfBuckets - 1) + CurrentBucket++; + NextBucketBoundary += BucketProbability; + } + Buckets->Bucket[i] = CurrentBucket; + Buckets->ExpectedCount[CurrentBucket] += + (FLOAT32) (ProbabilityDelta * SampleCount); + LastProbDensity = ProbDensity; + } + // place any leftover probability into the last bucket + Buckets->ExpectedCount[CurrentBucket] += + (FLOAT32) ((0.5 - Probability) * SampleCount); + + // copy upper half of distribution to lower half + for (i = 0, j = BUCKETTABLESIZE - 1; i < j; i++, j--) + Buckets->Bucket[i] = + Mirror (Buckets->Bucket[j], Buckets->NumberOfBuckets); + + // copy upper half of expected counts to lower half + for (i = 0, j = Buckets->NumberOfBuckets - 1; i <= j; i++, j--) + Buckets->ExpectedCount[i] += Buckets->ExpectedCount[j]; + } + return (Buckets); +} // MakeBuckets + + +//--------------------------------------------------------------------------- +UINT16 OptimumNumberOfBuckets(UINT32 SampleCount) { +/* + ** Parameters: + ** SampleCount number of samples to be tested + ** Globals: + ** CountTable lookup table for number of samples + ** BucketsTable lookup table for necessary number of buckets + ** Operation: + ** This routine computes the optimum number of histogram + ** buckets that should be used in a chi-squared goodness of + ** fit test for the specified number of samples. The optimum + ** number is computed based on Table 4.1 on pg. 147 of + ** "Measurement and Analysis of Random Data" by Bendat & Piersol. + ** Linear interpolation is used to interpolate between table + ** values. The table is intended for a 0.05 level of + ** significance (alpha). This routine assumes that it is + ** equally valid for other alpha's, which may not be true. + ** Return: + ** Optimum number of histogram buckets + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + UINT8 Last, Next; + FLOAT32 Slope; + + if (SampleCount < CountTable[0]) + return (BucketsTable[0]); + + for (Last = 0, Next = 1; Next < LOOKUPTABLESIZE; Last++, Next++) { + if (SampleCount <= CountTable[Next]) { + Slope = (FLOAT32) (BucketsTable[Next] - BucketsTable[Last]) / + (FLOAT32) (CountTable[Next] - CountTable[Last]); + return ((UINT16) (BucketsTable[Last] + + Slope * (SampleCount - CountTable[Last]))); + } + } + return (BucketsTable[Last]); +} // OptimumNumberOfBuckets + + +//--------------------------------------------------------------------------- +FLOAT64 +ComputeChiSquared (UINT16 DegreesOfFreedom, FLOAT64 Alpha) +/* + ** Parameters: + ** DegreesOfFreedom determines shape of distribution + ** Alpha probability of right tail + ** Globals: none + ** Operation: + ** This routine computes the chi-squared value which will + ** leave a cumulative probability of Alpha in the right tail + ** of a chi-squared distribution with the specified number of + ** degrees of freedom. Alpha must be between 0 and 1. + ** DegreesOfFreedom must be even. The routine maintains an + ** array of lists. Each list corresponds to a different + ** number of degrees of freedom. Each entry in the list + ** corresponds to a different alpha value and its corresponding + ** chi-squared value. Therefore, once a particular chi-squared + ** value is computed, it is stored in the list and never + ** needs to be computed again. + ** Return: Desired chi-squared value + ** Exceptions: none + ** History: 6/5/89, DSJ, Created. + */ +#define CHIACCURACY 0.01 +#define MINALPHA (1e-200) +{ + static LIST ChiWith[MAXDEGREESOFFREEDOM + 1]; + + CHISTRUCT *OldChiSquared; + CHISTRUCT SearchKey; + + // limit the minimum alpha that can be used - if alpha is too small + // it may not be possible to compute chi-squared. + if (Alpha < MINALPHA) + Alpha = MINALPHA; + if (Alpha > 1.0) + Alpha = 1.0; + if (Odd (DegreesOfFreedom)) + DegreesOfFreedom++; + + /* find the list of chi-squared values which have already been computed + for the specified number of degrees of freedom. Search the list for + the desired chi-squared. */ + SearchKey.Alpha = Alpha; + OldChiSquared = (CHISTRUCT *) first (search (ChiWith[DegreesOfFreedom], + &SearchKey, AlphaMatch)); + + if (OldChiSquared == NULL) { + OldChiSquared = NewChiStruct (DegreesOfFreedom, Alpha); + OldChiSquared->ChiSquared = Solve (ChiArea, OldChiSquared, + (FLOAT64) DegreesOfFreedom, + (FLOAT64) CHIACCURACY); + ChiWith[DegreesOfFreedom] = push (ChiWith[DegreesOfFreedom], + OldChiSquared); + } + else { + // further optimization might move OldChiSquared to front of list + } + + return (OldChiSquared->ChiSquared); + +} // ComputeChiSquared + + +//--------------------------------------------------------------------------- +FLOAT64 NormalDensity(INT32 x) { +/* + ** Parameters: + ** x number to compute the normal probability density for + ** Globals: + ** NormalMean mean of a discrete normal distribution + ** NormalVariance variance of a discrete normal distribution + ** NormalMagnitude magnitude of a discrete normal distribution + ** Operation: + ** This routine computes the probability density function + ** of a discrete normal distribution defined by the global + ** variables NormalMean, NormalVariance, and NormalMagnitude. + ** Normal magnitude could, of course, be computed in terms of + ** the normal variance but it is precomputed for efficiency. + ** Return: + ** The value of the normal distribution at x. + ** Exceptions: + ** None + ** History: + ** 6/4/89, DSJ, Created. + */ + FLOAT64 Distance; + + Distance = x - NormalMean; + return (NormalMagnitude * + exp (-0.5 * Distance * Distance / NormalVariance)); +} // NormalDensity + + +//--------------------------------------------------------------------------- +FLOAT64 UniformDensity(INT32 x) { +/* + ** Parameters: + ** x number to compute the uniform probability density for + ** Globals: + ** BUCKETTABLESIZE determines range of distribution + ** Operation: + ** This routine computes the probability density function + ** of a uniform distribution at the specified point. The + ** range of the distribution is from 0 to BUCKETTABLESIZE. + ** Return: + ** The value of the uniform distribution at x. + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + static FLOAT64 UniformDistributionDensity = (FLOAT64) 1.0 / BUCKETTABLESIZE; + + if ((x >= 0.0) && (x <= BUCKETTABLESIZE)) + return (UniformDistributionDensity); + else + return ((FLOAT64) 0.0); +} // UniformDensity + + +//--------------------------------------------------------------------------- +FLOAT64 Integral(FLOAT64 f1, FLOAT64 f2, FLOAT64 Dx) { +/* + ** Parameters: + ** f1 value of function at x1 + ** f2 value of function at x2 + ** Dx x2 - x1 (should always be positive) + ** Globals: + ** None + ** Operation: + ** This routine computes a trapezoidal approximation to the + ** integral of a function over a small delta in x. + ** Return: + ** Approximation of the integral of the function from x1 to x2. + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + return ((f1 + f2) * Dx / 2.0); +} // Integral + + +//--------------------------------------------------------------------------- +void FillBuckets(BUCKETS *Buckets, + CLUSTER *Cluster, + UINT16 Dim, + PARAM_DESC *ParamDesc, + FLOAT32 Mean, + FLOAT32 StdDev) { +/* + ** Parameters: + ** Buckets histogram buckets to count samples + ** Cluster cluster whose samples are being analyzed + ** Dim dimension of samples which is being analyzed + ** ParamDesc description of the dimension + ** Mean "mean" of the distribution + ** StdDev "standard deviation" of the distribution + ** Globals: + ** None + ** Operation: + ** This routine counts the number of cluster samples which + ** fall within the various histogram buckets in Buckets. Only + ** one dimension of each sample is examined. The exact meaning + ** of the Mean and StdDev parameters depends on the + ** distribution which is being analyzed (this info is in the + ** Buckets data structure). For normal distributions, Mean + ** and StdDev have the expected meanings. For uniform and + ** random distributions the Mean is the center point of the + ** range and the StdDev is 1/2 the range. A dimension with + ** zero standard deviation cannot be statistically analyzed. + ** In this case, a pseudo-analysis is used. + ** Return: + ** None (the Buckets data structure is filled in) + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + UINT16 BucketID; + int i; + LIST SearchState; + SAMPLE *Sample; + + // initialize the histogram bucket counts to 0 + for (i = 0; i < Buckets->NumberOfBuckets; i++) + Buckets->Count[i] = 0; + + if (StdDev == 0.0) { + /* if the standard deviation is zero, then we can't statistically + analyze the cluster. Use a pseudo-analysis: samples exactly on + the mean are distributed evenly across all buckets. Samples greater + than the mean are placed in the last bucket; samples less than the + mean are placed in the first bucket. */ + + InitSampleSearch(SearchState, Cluster); + i = 0; + while ((Sample = NextSample (&SearchState)) != NULL) { + if (Sample->Mean[Dim] > Mean) + BucketID = Buckets->NumberOfBuckets - 1; + else if (Sample->Mean[Dim] < Mean) + BucketID = 0; + else + BucketID = i; + Buckets->Count[BucketID] += 1; + i++; + if (i >= Buckets->NumberOfBuckets) + i = 0; + } + } + else { + // search for all samples in the cluster and add to histogram buckets + InitSampleSearch(SearchState, Cluster); + while ((Sample = NextSample (&SearchState)) != NULL) { + switch (Buckets->Distribution) { + case normal: + BucketID = NormalBucket (ParamDesc, Sample->Mean[Dim], + Mean, StdDev); + break; + case D_random: + case uniform: + BucketID = UniformBucket (ParamDesc, Sample->Mean[Dim], + Mean, StdDev); + break; + default: + BucketID = 0; + } + Buckets->Count[Buckets->Bucket[BucketID]] += 1; + } + } +} // FillBuckets + + +//---------------------------------------------------------------------------*/ +UINT16 NormalBucket(PARAM_DESC *ParamDesc, + FLOAT32 x, + FLOAT32 Mean, + FLOAT32 StdDev) { +/* + ** Parameters: + ** ParamDesc used to identify circular dimensions + ** x value to be normalized + ** Mean mean of normal distribution + ** StdDev standard deviation of normal distribution + ** Globals: + ** NormalMean mean of discrete normal distribution + ** NormalStdDev standard deviation of discrete normal dist. + ** BUCKETTABLESIZE range of the discrete distribution + ** Operation: + ** This routine determines which bucket x falls into in the + ** discrete normal distribution defined by NormalMean + ** and NormalStdDev. x values which exceed the range of + ** the discrete distribution are clipped. + ** Return: + ** Bucket number into which x falls + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + FLOAT32 X; + + // wraparound circular parameters if necessary + if (ParamDesc->Circular) { + if (x - Mean > ParamDesc->HalfRange) + x -= ParamDesc->Range; + else if (x - Mean < -ParamDesc->HalfRange) + x += ParamDesc->Range; + } + + X = ((x - Mean) / StdDev) * NormalStdDev + NormalMean; + if (X < 0) + return ((UINT16) 0); + if (X > BUCKETTABLESIZE - 1) + return ((UINT16) (BUCKETTABLESIZE - 1)); + return ((UINT16) floor ((FLOAT64) X)); +} // NormalBucket + + +//--------------------------------------------------------------------------- +UINT16 UniformBucket(PARAM_DESC *ParamDesc, + FLOAT32 x, + FLOAT32 Mean, + FLOAT32 StdDev) { +/* + ** Parameters: + ** ParamDesc used to identify circular dimensions + ** x value to be normalized + ** Mean center of range of uniform distribution + ** StdDev 1/2 the range of the uniform distribution + ** Globals: + ** BUCKETTABLESIZE range of the discrete distribution + ** Operation: + ** This routine determines which bucket x falls into in the + ** discrete uniform distribution defined by + ** BUCKETTABLESIZE. x values which exceed the range of + ** the discrete distribution are clipped. + ** Return: + ** Bucket number into which x falls + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + FLOAT32 X; + + // wraparound circular parameters if necessary + if (ParamDesc->Circular) { + if (x - Mean > ParamDesc->HalfRange) + x -= ParamDesc->Range; + else if (x - Mean < -ParamDesc->HalfRange) + x += ParamDesc->Range; + } + + X = ((x - Mean) / (2 * StdDev) * BUCKETTABLESIZE + BUCKETTABLESIZE / 2.0); + if (X < 0) + return ((UINT16) 0); + if (X > BUCKETTABLESIZE - 1) + return ((UINT16) (BUCKETTABLESIZE - 1)); + return ((UINT16) floor ((FLOAT64) X)); +} // UniformBucket + + +//--------------------------------------------------------------------------- +BOOL8 DistributionOK(BUCKETS *Buckets) { +/* + ** Parameters: + ** Buckets histogram data to perform chi-square test on + ** Globals: + ** None + ** Operation: + ** This routine performs a chi-square goodness of fit test + ** on the histogram data in the Buckets data structure. TRUE + ** is returned if the histogram matches the probability + ** distribution which was specified when the Buckets + ** structure was originally created. Otherwise FALSE is + ** returned. + ** Return: + ** TRUE if samples match distribution, FALSE otherwise + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + FLOAT32 FrequencyDifference; + FLOAT32 TotalDifference; + int i; + + // compute how well the histogram matches the expected histogram + TotalDifference = 0.0; + for (i = 0; i < Buckets->NumberOfBuckets; i++) { + FrequencyDifference = Buckets->Count[i] - Buckets->ExpectedCount[i]; + TotalDifference += (FrequencyDifference * FrequencyDifference) / + Buckets->ExpectedCount[i]; + } + + // test to see if the difference is more than expected + if (TotalDifference > Buckets->ChiSquared) + return (FALSE); + else + return (TRUE); +} // DistributionOK + + +//--------------------------------------------------------------------------- +void FreeStatistics(STATISTICS *Statistics) { +/* + ** Parameters: + ** Statistics pointer to data structure to be freed + ** Globals: + ** None + ** Operation: + ** This routine frees the memory used by the statistics + ** data structure. + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + memfree (Statistics->CoVariance); + memfree (Statistics->Min); + memfree (Statistics->Max); + memfree(Statistics); +} // FreeStatistics + + +//--------------------------------------------------------------------------- +void FreeBuckets(BUCKETS *Buckets) { +/* + ** Parameters: + ** Buckets pointer to data structure to be freed + ** Globals: none + ** Operation: + ** This routine places the specified histogram data structure + ** at the front of a list of histograms so that it can be + ** reused later if necessary. A separate list is maintained + ** for each different type of distribution. + ** Return: none + ** Exceptions: none + ** History: 6/5/89, DSJ, Created. + */ + int Dist; + + if (Buckets != NULL) { + Dist = (int) Buckets->Distribution; + OldBuckets[Dist] = (LIST) push (OldBuckets[Dist], Buckets); + } + +} // FreeBuckets + + +//--------------------------------------------------------------------------- +void FreeCluster(CLUSTER *Cluster) { +/* + ** Parameters: + ** Cluster pointer to cluster to be freed + ** Globals: + ** None + ** Operation: + ** This routine frees the memory consumed by the specified + ** cluster and all of its subclusters. This is done by + ** recursive calls to FreeCluster(). + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 6/6/89, DSJ, Created. + */ + if (Cluster != NULL) { + FreeCluster (Cluster->Left); + FreeCluster (Cluster->Right); + memfree(Cluster); + } +} // FreeCluster + + +//--------------------------------------------------------------------------- +UINT16 DegreesOfFreedom(DISTRIBUTION Distribution, UINT16 HistogramBuckets) { +/* + ** Parameters: + ** Distribution distribution being tested for + ** HistogramBuckets number of buckets in chi-square test + ** Globals: none + ** Operation: + ** This routine computes the degrees of freedom that should + ** be used in a chi-squared test with the specified number of + ** histogram buckets. The result is always rounded up to + ** the next even number so that the value of chi-squared can be + ** computed more easily. This will cause the value of + ** chi-squared to be higher than the optimum value, resulting + ** in the chi-square test being more lenient than optimum. + ** Return: The number of degrees of freedom for a chi-square test + ** Exceptions: none + ** History: Thu Aug 3 14:04:18 1989, DSJ, Created. + */ + static UINT8 DegreeOffsets[] = { 3, 3, 1 }; + + UINT16 AdjustedNumBuckets; + + AdjustedNumBuckets = HistogramBuckets - DegreeOffsets[(int) Distribution]; + if (Odd (AdjustedNumBuckets)) + AdjustedNumBuckets++; + return (AdjustedNumBuckets); + +} // DegreesOfFreedom + + +//--------------------------------------------------------------------------- +int NumBucketsMatch(void *arg1, //BUCKETS *Histogram, + void *arg2) { //UINT16 *DesiredNumberOfBuckets) +/* + ** Parameters: + ** Histogram current histogram being tested for a match + ** DesiredNumberOfBuckets match key + ** Globals: none + ** Operation: + ** This routine is used to search a list of histogram data + ** structures to find one with the specified number of + ** buckets. It is called by the list search routines. + ** Return: TRUE if Histogram matches DesiredNumberOfBuckets + ** Exceptions: none + ** History: Thu Aug 3 14:17:33 1989, DSJ, Created. + */ + BUCKETS *Histogram = (BUCKETS *) arg1; + UINT16 *DesiredNumberOfBuckets = (UINT16 *) arg2; + + return (*DesiredNumberOfBuckets == Histogram->NumberOfBuckets); + +} // NumBucketsMatch + + +//--------------------------------------------------------------------------- +int ListEntryMatch(void *arg1, //ListNode + void *arg2) { //Key +/* + ** Parameters: none + ** Globals: none + ** Operation: + ** This routine is used to search a list for a list node + ** whose contents match Key. It is called by the list + ** delete_d routine. + ** Return: TRUE if ListNode matches Key + ** Exceptions: none + ** History: Thu Aug 3 14:23:58 1989, DSJ, Created. + */ + return (arg1 == arg2); + +} // ListEntryMatch + + +//--------------------------------------------------------------------------- +void AdjustBuckets(BUCKETS *Buckets, UINT32 NewSampleCount) { +/* + ** Parameters: + ** Buckets histogram data structure to adjust + ** NewSampleCount new sample count to adjust to + ** Globals: none + ** Operation: + ** This routine multiplies each ExpectedCount histogram entry + ** by NewSampleCount/OldSampleCount so that the histogram + ** is now adjusted to the new sample count. + ** Return: none + ** Exceptions: none + ** History: Thu Aug 3 14:31:14 1989, DSJ, Created. + */ + int i; + FLOAT64 AdjustFactor; + + AdjustFactor = (((FLOAT64) NewSampleCount) / + ((FLOAT64) Buckets->SampleCount)); + + for (i = 0; i < Buckets->NumberOfBuckets; i++) { + Buckets->ExpectedCount[i] *= AdjustFactor; + } + + Buckets->SampleCount = NewSampleCount; + +} // AdjustBuckets + + +//--------------------------------------------------------------------------- +void InitBuckets(BUCKETS *Buckets) { +/* + ** Parameters: + ** Buckets histogram data structure to init + ** Globals: none + ** Operation: + ** This routine sets the bucket counts in the specified histogram + ** to zero. + ** Return: none + ** Exceptions: none + ** History: Thu Aug 3 14:31:14 1989, DSJ, Created. + */ + int i; + + for (i = 0; i < Buckets->NumberOfBuckets; i++) { + Buckets->Count[i] = 0; + } + +} // InitBuckets + + +//--------------------------------------------------------------------------- +int AlphaMatch(void *arg1, //CHISTRUCT *ChiStruct, + void *arg2) { //CHISTRUCT *SearchKey) +/* + ** Parameters: + ** ChiStruct chi-squared struct being tested for a match + ** SearchKey chi-squared struct that is the search key + ** Globals: none + ** Operation: + ** This routine is used to search a list of structures which + ** hold pre-computed chi-squared values for a chi-squared + ** value whose corresponding alpha field matches the alpha + ** field of SearchKey. + ** It is called by the list search routines. + ** Return: TRUE if ChiStruct's Alpha matches SearchKey's Alpha + ** Exceptions: none + ** History: Thu Aug 3 14:17:33 1989, DSJ, Created. + */ + CHISTRUCT *ChiStruct = (CHISTRUCT *) arg1; + CHISTRUCT *SearchKey = (CHISTRUCT *) arg2; + + return (ChiStruct->Alpha == SearchKey->Alpha); + +} // AlphaMatch + + +//--------------------------------------------------------------------------- +CHISTRUCT *NewChiStruct(UINT16 DegreesOfFreedom, FLOAT64 Alpha) { +/* + ** Parameters: + ** DegreesOfFreedom degrees of freedom for new chi value + ** Alpha confidence level for new chi value + ** Globals: none + ** Operation: + ** This routine allocates a new data structure which is used + ** to hold a chi-squared value along with its associated + ** number of degrees of freedom and alpha value. + ** Return: none + ** Exceptions: none + ** History: Fri Aug 4 11:04:59 1989, DSJ, Created. + */ + CHISTRUCT *NewChiStruct; + + NewChiStruct = (CHISTRUCT *) Emalloc (sizeof (CHISTRUCT)); + NewChiStruct->DegreesOfFreedom = DegreesOfFreedom; + NewChiStruct->Alpha = Alpha; + return (NewChiStruct); + +} // NewChiStruct + + +//--------------------------------------------------------------------------- +FLOAT64 +Solve (SOLVEFUNC Function, +void *FunctionParams, FLOAT64 InitialGuess, FLOAT64 Accuracy) +/* + ** Parameters: + ** Function function whose zero is to be found + ** FunctionParams arbitrary data to pass to function + ** InitialGuess point to start solution search at + ** Accuracy maximum allowed error + ** Globals: none + ** Operation: + ** This routine attempts to find an x value at which Function + ** goes to zero (i.e. a root of the function ). It will only + ** work correctly if a solution actually exists and there + ** are no extrema between the solution and the InitialGuess. + ** The algorithms used are extremely primitive. + ** Return: Solution of function ( x for which f(x) = 0 ). + ** Exceptions: none + ** History: Fri Aug 4 11:08:59 1989, DSJ, Created. + */ +#define INITIALDELTA 0.1 +#define DELTARATIO 0.1 +{ + FLOAT64 x; + FLOAT64 f; + FLOAT64 Slope; + FLOAT64 Delta; + FLOAT64 NewDelta; + FLOAT64 xDelta; + FLOAT64 LastPosX, LastNegX; + + x = InitialGuess; + Delta = INITIALDELTA; + LastPosX = MAX_FLOAT32; + LastNegX = -MAX_FLOAT32; + f = (*Function) ((CHISTRUCT *) FunctionParams, x); + while (Abs (LastPosX - LastNegX) > Accuracy) { + // keep track of outer bounds of current estimate + if (f < 0) + LastNegX = x; + else + LastPosX = x; + + // compute the approx. slope of f(x) at the current point + Slope = + ((*Function) ((CHISTRUCT *) FunctionParams, x + Delta) - f) / Delta; + + // compute the next solution guess */ + xDelta = f / Slope; + x -= xDelta; + + // reduce the delta used for computing slope to be a fraction of + //the amount moved to get to the new guess + NewDelta = Abs (xDelta) * DELTARATIO; + if (NewDelta < Delta) + Delta = NewDelta; + + // compute the value of the function at the new guess + f = (*Function) ((CHISTRUCT *) FunctionParams, x); + } + return (x); + +} // Solve + + +//--------------------------------------------------------------------------- +FLOAT64 ChiArea(CHISTRUCT *ChiParams, FLOAT64 x) { +/* + ** Parameters: + ** ChiParams contains degrees of freedom and alpha + ** x value of chi-squared to evaluate + ** Globals: none + ** Operation: + ** This routine computes the area under a chi density curve + ** from 0 to x, minus the desired area under the curve. The + ** number of degrees of freedom of the chi curve is specified + ** in the ChiParams structure. The desired area is also + ** specified in the ChiParams structure as Alpha ( or 1 minus + ** the desired area ). This routine is intended to be passed + ** to the Solve() function to find the value of chi-squared + ** which will yield a desired area under the right tail of + ** the chi density curve. The function will only work for + ** even degrees of freedom. The equations are based on + ** integrating the chi density curve in parts to obtain + ** a series that can be used to compute the area under the + ** curve. + ** Return: Error between actual and desired area under the chi curve. + ** Exceptions: none + ** History: Fri Aug 4 12:48:41 1989, DSJ, Created. + */ + int i, N; + FLOAT64 SeriesTotal; + FLOAT64 Denominator; + FLOAT64 PowerOfx; + + N = ChiParams->DegreesOfFreedom / 2 - 1; + SeriesTotal = 1; + Denominator = 1; + PowerOfx = 1; + for (i = 1; i <= N; i++) { + Denominator *= 2 * i; + PowerOfx *= x; + SeriesTotal += PowerOfx / Denominator; + } + return ((SeriesTotal * exp (-0.5 * x)) - ChiParams->Alpha); + +} // ChiArea + + +//--------------------------------------------------------------------------- +BOOL8 +MultipleCharSamples (CLUSTERER * Clusterer, +CLUSTER * Cluster, FLOAT32 MaxIllegal) +/* + ** Parameters: + ** Clusterer data structure holding cluster tree + ** Cluster cluster containing samples to be tested + ** MaxIllegal max percentage of samples allowed to have + ** more than 1 feature in the cluster + ** Globals: none + ** Operation: + ** This routine looks at all samples in the specified cluster. + ** It computes a running estimate of the percentage of the + ** charaters which have more than 1 sample in the cluster. + ** When this percentage exceeds MaxIllegal, TRUE is returned. + ** Otherwise FALSE is returned. The CharID + ** fields must contain integers which identify the training + ** characters which were used to generate the sample. One + ** integer is used for each sample. The NumChar field in + ** the Clusterer must contain the number of characters in the + ** training set. All CharID fields must be between 0 and + ** NumChar-1. The main function of this routine is to help + ** identify clusters which need to be split further, i.e. if + ** numerous training characters have 2 or more features which are + ** contained in the same cluster, then the cluster should be + ** split. + ** Return: TRUE if the cluster should be split, FALSE otherwise. + ** Exceptions: none + ** History: Wed Aug 30 11:13:05 1989, DSJ, Created. + ** 2/22/90, DSJ, Added MaxIllegal control rather than always + ** splitting illegal clusters. + */ +#define ILLEGAL_CHAR 2 +{ + static BOOL8 *CharFlags = NULL; + static INT32 NumFlags = 0; + int i; + LIST SearchState; + SAMPLE *Sample; + INT32 CharID; + INT32 NumCharInCluster; + INT32 NumIllegalInCluster; + FLOAT32 PercentIllegal; + + // initial estimate assumes that no illegal chars exist in the cluster + NumCharInCluster = Cluster->SampleCount; + NumIllegalInCluster = 0; + + if (Clusterer->NumChar > NumFlags) { + if (CharFlags != NULL) + memfree(CharFlags); + NumFlags = Clusterer->NumChar; + CharFlags = (BOOL8 *) Emalloc (NumFlags * sizeof (BOOL8)); + } + + for (i = 0; i < NumFlags; i++) + CharFlags[i] = FALSE; + + // find each sample in the cluster and check if we have seen it before + InitSampleSearch(SearchState, Cluster); + while ((Sample = NextSample (&SearchState)) != NULL) { + CharID = Sample->CharID; + if (CharFlags[CharID] == FALSE) { + CharFlags[CharID] = TRUE; + } + else { + if (CharFlags[CharID] == TRUE) { + NumIllegalInCluster++; + CharFlags[CharID] = ILLEGAL_CHAR; + } + NumCharInCluster--; + PercentIllegal = (FLOAT32) NumIllegalInCluster / NumCharInCluster; + if (PercentIllegal > MaxIllegal) + return (TRUE); + } + } + return (FALSE); + +} // MultipleCharSamples + +// Compute the inverse of a matrix using LU decomposition with partial pivoting. +// The return value is the sum of norms of the off-diagonal terms of the +// product of a and inv. (A measure of the error.) +double InvertMatrix(const float* input, int size, float* inv) { + double** U; // The upper triangular array. + double* Umem; + double** U_inv; // The inverse of U. + double* U_invmem; + double** L; // The lower triangular array. + double* Lmem; + + // Allocate memory for the 2D arrays. + ALLOC_2D_ARRAY(size, size, Umem, U, double); + ALLOC_2D_ARRAY(size, size, U_invmem, U_inv, double); + ALLOC_2D_ARRAY(size, size, Lmem, L, double); + + // Initialize the working matrices. U starts as input, L as I and U_inv as O. + int row; + int col; + for (row = 0; row < size; row++) { + for (col = 0; col < size; col++) { + U[row][col] = input[row*size + col]; + L[row][col] = row == col ? 1.0 : 0.0; + U_inv[row][col] = 0.0; + } + } + + // Compute forward matrix by inversion by LU decomposition of input. + for (col = 0; col < size; ++col) { + // Find best pivot + int best_row = 0; + double best_pivot = -1.0; + for (row = col; row < size; ++row) { + if (Abs(U[row][col]) > best_pivot) { + best_pivot = Abs(U[row][col]); + best_row = row; + } + } + // Exchange pivot rows. + if (best_row != col) { + for (int k = 0; k < size; ++k) { + double tmp = U[best_row][k]; + U[best_row][k] = U[col][k]; + U[col][k] = tmp; + tmp = L[best_row][k]; + L[best_row][k] = L[col][k]; + L[col][k] = tmp; + } + } + // Now do the pivot itself. + for (row = col + 1; row < size; ++row) { + double ratio = -U[row][col] / U[col][col]; + for (int j = col; j < size; ++j) { + U[row][j] += U[col][j] * ratio; + } + for (int k = 0; k < size; ++k) { + L[row][k] += L[col][k] * ratio; + } + } + } + // Next invert U. + for (col = 0; col < size; ++col) { + U_inv[col][col] = 1.0 / U[col][col]; + for (row = col - 1; row >= 0; --row) { + double total = 0.0; + for (int k = col; k > row; --k) { + total += U[row][k] * U_inv[k][col]; + } + U_inv[row][col] = -total / U[row][row]; + } + } + // Now the answer is U_inv.L. + for (row = 0; row < size; row++) { + for (col = 0; col < size; col++) { + double sum = 0.0; + for (int k = row; k < size; ++k) { + sum += U_inv[row][k] * L[k][col]; + } + inv[row*size + col] = sum; + } + } + // Check matrix product. + double error_sum = 0.0; + for (row = 0; row < size; row++) { + for (col = 0; col < size; col++) { + double sum = 0.0; + for (int k = 0; k < size; ++k) { + sum += input[row*size + k] * inv[k *size + col]; + } + if (row != col) { + error_sum += Abs(sum); + } + } + } + return error_sum; +} + + diff --git a/classify/cluster.h b/classify/cluster.h new file mode 100644 index 0000000000..547f2d96ce --- /dev/null +++ b/classify/cluster.h @@ -0,0 +1,149 @@ +/****************************************************************************** + ** Filename: cluster.h + ** Purpose: Definition of feature space clustering routines + ** Author: Dan Johnson + ** History: 5/29/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef CLUSTER_H +#define CLUSTER_H + +#include "kdtree.h" +#include "oldlist.h" + +/*---------------------------------------------------------------------- + Types +----------------------------------------------------------------------*/ +typedef struct sample +{ + unsigned Clustered:1; // TRUE if included in a higher cluster + unsigned Prototype:1; // TRUE if cluster represented by a proto + unsigned SampleCount:30; // number of samples in this cluster + struct sample *Left; // ptr to left sub-cluster + struct sample *Right; // ptr to right sub-cluster + INT32 CharID; // identifier of char sample came from + FLOAT32 Mean[1]; // mean of cluster - SampleSize floats +} + + +CLUSTER; + +typedef CLUSTER SAMPLE; // can refer to as either sample or cluster + +typedef enum { + spherical, elliptical, mixed, automatic +} + + +PROTOSTYLE; + +typedef struct // parameters to control clustering +{ + PROTOSTYLE ProtoStyle; // specifies types of protos to be made + FLOAT32 MinSamples; // min # of samples per proto - % of total + FLOAT32 MaxIllegal; // max percentage of samples in a cluster which have + // more than 1 feature in that cluster + FLOAT32 Independence; // desired independence between dimensions + FLOAT64 Confidence; // desired confidence in prototypes created +} + + +CLUSTERCONFIG; + +typedef enum { + normal, uniform, D_random +} + + +DISTRIBUTION; + +typedef union +{ + FLOAT32 Spherical; + FLOAT32 *Elliptical; + +} + + +FLOATUNION; + +typedef struct proto +{ + unsigned Significant:1; // TRUE if prototype is significant + unsigned Style:2; // spherical, elliptical, or mixed + unsigned NumSamples:29; // number of samples in the cluster + CLUSTER *Cluster; // ptr to cluster which made prototype + DISTRIBUTION *Distrib; // different distribution for each dimension + FLOAT32 *Mean; // prototype mean + FLOAT32 TotalMagnitude; // total magnitude over all dimensions + FLOAT32 LogMagnitude; // log base e of TotalMagnitude + FLOATUNION Variance; // prototype variance + FLOATUNION Magnitude; // magnitude of density function + FLOATUNION Weight; // weight of density function +} + + +PROTOTYPE; + +typedef struct +{ + INT16 SampleSize; // number of parameters per sample + PARAM_DESC *ParamDesc; // description of each parameter + INT32 NumberOfSamples; // total number of samples being clustered + KDTREE *KDTree; // for optimal nearest neighbor searching + CLUSTER *Root; // ptr to root cluster of cluster tree + LIST ProtoList; // list of prototypes + INT32 NumChar; // # of characters represented by samples +} + + +CLUSTERER; + +typedef struct +{ + INT32 NumSamples; // number of samples in list + INT32 MaxNumSamples; // maximum size of list + SAMPLE *Sample[1]; // array of ptrs to sample data structures +} + + +SAMPLELIST; + +// low level cluster tree analysis routines. +#define InitSampleSearch(S,C) (((C)==NULL)?(S=NIL):(S=push(NIL,(C)))) + +/*-------------------------------------------------------------------------- + Public Function Prototypes +--------------------------------------------------------------------------*/ +CLUSTERER *MakeClusterer (INT16 SampleSize, PARAM_DESC ParamDesc[]); + +SAMPLE *MakeSample (CLUSTERER * Clusterer, FLOAT32 Feature[], INT32 CharID); + +LIST ClusterSamples(CLUSTERER *Clusterer, CLUSTERCONFIG *Config); + +void FreeClusterer(CLUSTERER *Clusterer); + +void FreeProtoList(LIST *ProtoList); + +void FreePrototype(void *arg); //PROTOTYPE *Prototype); + +CLUSTER *NextSample(LIST *SearchState); + +FLOAT32 Mean(PROTOTYPE *Proto, UINT16 Dimension); + +FLOAT32 StandardDeviation(PROTOTYPE *Proto, UINT16 Dimension); + +//--------------Global Data Definitions and Declarations--------------------------- +// define errors that can be trapped +#define ALREADYCLUSTERED 4000 +#endif diff --git a/classify/clusttool.cpp b/classify/clusttool.cpp new file mode 100644 index 0000000000..70e588ef06 --- /dev/null +++ b/classify/clusttool.cpp @@ -0,0 +1,507 @@ +/****************************************************************************** + ** Filename: clustertool.c + ** Purpose: Misc. tools for use with the clustering routines + ** Author: Dan Johnson + ** History: 6/6/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ + +//--------------------------Include Files---------------------------------- +#include "clusttool.h" +#include "const.h" +#include "danerror.h" +#include "emalloc.h" +#include "scanutils.h" +#include +#include + +//---------------Global Data Definitions and Declarations-------------------- +#define TOKENSIZE 80 //max size of tokens read from an input file +#define MAXSAMPLESIZE 65535 //max num of dimensions in feature space +//#define MAXBLOCKSIZE 65535 //max num of samples in a character (block size) + +/*--------------------------------------------------------------------------- + Public Code +-----------------------------------------------------------------------------*/ +/** ReadSampleSize *********************************************************** +Parameters: File open text file to read sample size from +Globals: None +Operation: This routine reads a single integer from the specified + file and checks to ensure that it is between 0 and + MAXSAMPLESIZE. +Return: Sample size +Exceptions: ILLEGALSAMPLESIZE illegal format or range +History: 6/6/89, DSJ, Created. +******************************************************************************/ +UINT16 ReadSampleSize(FILE *File) { + int SampleSize; + + if ((fscanf (File, "%d", &SampleSize) != 1) || + (SampleSize < 0) || (SampleSize > MAXSAMPLESIZE)) + DoError (ILLEGALSAMPLESIZE, "Illegal sample size"); + return (SampleSize); +} // ReadSampleSize + + +/** ReadParamDesc ************************************************************* +Parameters: File open text file to read N parameter descriptions from + N number of parameter descriptions to read +Globals: None +Operation: This routine reads textual descriptions of sets of parameters + which describe the characteristics of feature dimensions. +Return: Pointer to an array of parameter descriptors. +Exceptions: ILLEGALCIRCULARSPEC + ILLEGALESSENTIALSPEC + ILLEGALMINMAXSPEC +History: 6/6/89, DSJ, Created. +******************************************************************************/ +PARAM_DESC *ReadParamDesc(FILE *File, UINT16 N) { + int i; + PARAM_DESC *ParamDesc; + char Token[TOKENSIZE]; + + ParamDesc = (PARAM_DESC *) Emalloc (N * sizeof (PARAM_DESC)); + for (i = 0; i < N; i++) { + if (fscanf (File, "%s", Token) != 1) + DoError (ILLEGALCIRCULARSPEC, + "Illegal circular/linear specification"); + if (Token[0] == 'c') + ParamDesc[i].Circular = TRUE; + else + ParamDesc[i].Circular = FALSE; + + if (fscanf (File, "%s", Token) != 1) + DoError (ILLEGALESSENTIALSPEC, + "Illegal essential/non-essential spec"); + if (Token[0] == 'e') + ParamDesc[i].NonEssential = FALSE; + else + ParamDesc[i].NonEssential = TRUE; + if (fscanf (File, "%f%f", &(ParamDesc[i].Min), &(ParamDesc[i].Max)) != + 2) + DoError (ILLEGALMINMAXSPEC, "Illegal min or max specification"); + ParamDesc[i].Range = ParamDesc[i].Max - ParamDesc[i].Min; + ParamDesc[i].HalfRange = ParamDesc[i].Range / 2; + ParamDesc[i].MidRange = (ParamDesc[i].Max + ParamDesc[i].Min) / 2; + } + return (ParamDesc); +} // ReadParamDesc + + +/** ReadPrototype ************************************************************* +Parameters: File open text file to read prototype from + N number of dimensions used in prototype +Globals: None +Operation: This routine reads a textual description of a prototype from + the specified file. +Return: List of prototypes +Exceptions: ILLEGALSIGNIFICANCESPEC + ILLEGALSAMPLECOUNT + ILLEGALMEANSPEC + ILLEGALVARIANCESPEC + ILLEGALDISTRIBUTION +History: 6/6/89, DSJ, Created. +******************************************************************************/ +PROTOTYPE *ReadPrototype(FILE *File, UINT16 N) { + char Token[TOKENSIZE]; + int Status; + PROTOTYPE *Proto; + int SampleCount; + int i; + + if ((Status = fscanf (File, "%s", Token)) == 1) { + Proto = (PROTOTYPE *) Emalloc (sizeof (PROTOTYPE)); + Proto->Cluster = NULL; + if (Token[0] == 's') + Proto->Significant = TRUE; + else + Proto->Significant = FALSE; + + Proto->Style = ReadProtoStyle (File); + + if ((fscanf (File, "%d", &SampleCount) != 1) || (SampleCount < 0)) + DoError (ILLEGALSAMPLECOUNT, "Illegal sample count"); + Proto->NumSamples = SampleCount; + + Proto->Mean = ReadNFloats (File, N, NULL); + if (Proto->Mean == NULL) + DoError (ILLEGALMEANSPEC, "Illegal prototype mean"); + + switch (Proto->Style) { + case spherical: + if (ReadNFloats (File, 1, &(Proto->Variance.Spherical)) == NULL) + DoError (ILLEGALVARIANCESPEC, "Illegal prototype variance"); + Proto->Magnitude.Spherical = + 1.0 / sqrt ((double) (2.0 * PI * Proto->Variance.Spherical)); + Proto->TotalMagnitude = + pow (Proto->Magnitude.Spherical, (double) N); + Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); + Proto->Weight.Spherical = 1.0 / Proto->Variance.Spherical; + Proto->Distrib = NULL; + break; + case elliptical: + Proto->Variance.Elliptical = ReadNFloats (File, N, NULL); + if (Proto->Variance.Elliptical == NULL) + DoError (ILLEGALVARIANCESPEC, "Illegal prototype variance"); + Proto->Magnitude.Elliptical = + (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + Proto->Weight.Elliptical = + (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + Proto->TotalMagnitude = 1.0; + for (i = 0; i < N; i++) { + Proto->Magnitude.Elliptical[i] = + 1.0 / + sqrt ((double) (2.0 * PI * Proto->Variance.Elliptical[i])); + Proto->Weight.Elliptical[i] = + 1.0 / Proto->Variance.Elliptical[i]; + Proto->TotalMagnitude *= Proto->Magnitude.Elliptical[i]; + } + Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); + Proto->Distrib = NULL; + break; + case mixed: + Proto->Distrib = + (DISTRIBUTION *) Emalloc (N * sizeof (DISTRIBUTION)); + for (i = 0; i < N; i++) { + if (fscanf (File, "%s", Token) != 1) + DoError (ILLEGALDISTRIBUTION, + "Illegal prototype distribution"); + switch (Token[0]) { + case 'n': + Proto->Distrib[i] = normal; + break; + case 'u': + Proto->Distrib[i] = uniform; + break; + case 'r': + Proto->Distrib[i] = D_random; + break; + default: + DoError (ILLEGALDISTRIBUTION, + "Illegal prototype distribution"); + } + } + Proto->Variance.Elliptical = ReadNFloats (File, N, NULL); + if (Proto->Variance.Elliptical == NULL) + DoError (ILLEGALVARIANCESPEC, "Illegal prototype variance"); + Proto->Magnitude.Elliptical = + (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + Proto->Weight.Elliptical = + (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + Proto->TotalMagnitude = 1.0; + for (i = 0; i < N; i++) { + switch (Proto->Distrib[i]) { + case normal: + Proto->Magnitude.Elliptical[i] = 1.0 / + sqrt ((double) + (2.0 * PI * Proto->Variance.Elliptical[i])); + Proto->Weight.Elliptical[i] = + 1.0 / Proto->Variance.Elliptical[i]; + break; + case uniform: + case D_random: + Proto->Magnitude.Elliptical[i] = 1.0 / + (2.0 * Proto->Variance.Elliptical[i]); + break; + } + Proto->TotalMagnitude *= Proto->Magnitude.Elliptical[i]; + } + Proto->LogMagnitude = log ((double) Proto->TotalMagnitude); + break; + } + return (Proto); + } + else if (Status == EOF) + return (NULL); + else { + DoError (ILLEGALSIGNIFICANCESPEC, "Illegal significance specification"); + return (NULL); + } +} // ReadPrototype + + +/* ReadProtoStyle ************************************************************* +Parameters: File open text file to read prototype style from +Globals: None +Operation: This routine reads an single token from the specified + text file and interprets it as a prototype specification. +Return: Prototype style read from text file +Exceptions: ILLEGALSTYLESPEC illegal prototype style specification +History: 6/8/89, DSJ, Created. +*******************************************************************************/ +PROTOSTYLE ReadProtoStyle(FILE *File) { + char Token[TOKENSIZE]; + PROTOSTYLE Style; + + if (fscanf (File, "%s", Token) != 1) + DoError (ILLEGALSTYLESPEC, "Illegal prototype style specification"); + switch (Token[0]) { + case 's': + Style = spherical; + break; + case 'e': + Style = elliptical; + break; + case 'm': + Style = mixed; + break; + case 'a': + Style = automatic; + break; + default: + Style = elliptical; + DoError (ILLEGALSTYLESPEC, "Illegal prototype style specification"); + } + return (Style); +} // ReadProtoStyle + + +/** ReadNFloats ************************************************************* +Parameters: File open text file to read floats from + N number of floats to read + Buffer pointer to buffer to place floats into +Globals: None +Operation: This routine reads N floats from the specified text file + and places them into Buffer. If Buffer is NULL, a buffer + is created and passed back to the caller. If EOF is + encountered before any floats can be read, NULL is + returned. +Return: Pointer to buffer holding floats or NULL if EOF +Exceptions: ILLEGALFLOAT +History: 6/6/89, DSJ, Created. +******************************************************************************/ +FLOAT32 * +ReadNFloats (FILE * File, UINT16 N, FLOAT32 Buffer[]) { + int i; + int NumFloatsRead; + + if (Buffer == NULL) + Buffer = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + + for (i = 0; i < N; i++) { + NumFloatsRead = fscanf (File, "%f", &(Buffer[i])); + if (NumFloatsRead != 1) { + if ((NumFloatsRead == EOF) && (i == 0)) + return (NULL); + else + DoError (ILLEGALFLOAT, "Illegal float specification"); + } + } + return (Buffer); +} // ReadNFloats + + +/** WriteParamDesc ************************************************************ +Parameters: File open text file to write param descriptors to + N number of param descriptors to write + ParamDesc array of param descriptors to write +Globals: None +Operation: This routine writes an array of dimension descriptors to + the specified text file. +Return: None +Exceptions: None +History: 6/6/89, DSJ, Created. +******************************************************************************/ +void +WriteParamDesc (FILE * File, UINT16 N, PARAM_DESC ParamDesc[]) { + int i; + + for (i = 0; i < N; i++) { + if (ParamDesc[i].Circular) + fprintf (File, "circular "); + else + fprintf (File, "linear "); + + if (ParamDesc[i].NonEssential) + fprintf (File, "non-essential "); + else + fprintf (File, "essential "); + + fprintf (File, "%10.6f %10.6f\n", ParamDesc[i].Min, ParamDesc[i].Max); + } +} // WriteParamDesc + + +/** WritePrototype ************************************************************ +Parameters: File open text file to write prototype to + N number of dimensions in feature space + Proto prototype to write out +Globals: None +Operation: This routine writes a textual description of a prototype + to the specified text file. +Return: None +Exceptions: None +History: 6/12/89, DSJ, Created. +*******************************************************************************/ +void WritePrototype(FILE *File, UINT16 N, PROTOTYPE *Proto) { + int i; + + if (Proto->Significant) + fprintf (File, "significant "); + else + fprintf (File, "insignificant "); + WriteProtoStyle (File, (PROTOSTYLE) Proto->Style); + fprintf (File, "%6d\n\t", Proto->NumSamples); + WriteNFloats (File, N, Proto->Mean); + fprintf (File, "\t"); + + switch (Proto->Style) { + case spherical: + WriteNFloats (File, 1, &(Proto->Variance.Spherical)); + break; + case elliptical: + WriteNFloats (File, N, Proto->Variance.Elliptical); + break; + case mixed: + for (i = 0; i < N; i++) + switch (Proto->Distrib[i]) { + case normal: + fprintf (File, " %9s", "normal"); + break; + case uniform: + fprintf (File, " %9s", "uniform"); + break; + case D_random: + fprintf (File, " %9s", "random"); + break; + } + fprintf (File, "\n\t"); + WriteNFloats (File, N, Proto->Variance.Elliptical); + } +} // WritePrototype + + +/** WriteNFloats *********************************************************** +Parameters: File open text file to write N floats to + N number of floats to write + Array array of floats to write +Globals: None +Operation: This routine writes a text representation of N floats from + an array to a file. All of the floats are placed on one line. +Return: None +Exceptions: None +History: 6/6/89, DSJ, Created. +****************************************************************************/ +void +WriteNFloats (FILE * File, UINT16 N, FLOAT32 Array[]) { + int i; + + for (i = 0; i < N; i++) + fprintf (File, " %9.6f", Array[i]); + fprintf (File, "\n"); +} // WriteNFloats + + +/** WriteProtoSyle ********************************************************** +Parameters: File open text file to write prototype style to + ProtoStyle prototype style to write +Globals: None +Operation: This routine writes to the specified text file a word + which represents the ProtoStyle. It does not append + a carriage return to the end. +Return: None +Exceptions: None +History: 6/8/89, DSJ, Created. +****************************************************************************/ +void WriteProtoStyle(FILE *File, PROTOSTYLE ProtoStyle) { + switch (ProtoStyle) { + case spherical: + fprintf (File, "spherical"); + break; + case elliptical: + fprintf (File, "elliptical"); + break; + case mixed: + fprintf (File, "mixed"); + break; + case automatic: + fprintf (File, "automatic"); + break; + } +} // WriteProtoStyle + +/*---------------------------------------------------------------------------*/ +void WriteProtoList( + FILE *File, + UINT16 N, + PARAM_DESC ParamDesc[], + LIST ProtoList, + BOOL8 WriteSigProtos, + BOOL8 WriteInsigProtos) + +/* +** Parameters: +** File open text file to write prototypes to +** N number of dimensions in feature space +** ParamDesc descriptions for each dimension +** ProtoList list of prototypes to be written +** WriteSigProtos TRUE to write out significant prototypes +** WriteInsigProtos TRUE to write out insignificants +** Globals: +** None +** Operation: +** This routine writes a textual description of each prototype +** in the prototype list to the specified file. It also +** writes a file header which includes the number of dimensions +** in feature space and the descriptions for each dimension. +** Return: +** None +** Exceptions: +** None +** History: +** 6/12/89, DSJ, Created. +*/ + +{ + PROTOTYPE *Proto; + + /* write file header */ + fprintf(File,"%0d\n",N); + WriteParamDesc(File,N,ParamDesc); + + /* write prototypes */ + iterate(ProtoList) + { + Proto = (PROTOTYPE *) first ( ProtoList ); + if (( Proto->Significant && WriteSigProtos ) || + ( ! Proto->Significant && WriteInsigProtos ) ) + WritePrototype( File, N, Proto ); + } +} /* WriteProtoList */ + +/** UniformRandomNumber ******************************************************** +Parameters: MMin lower range of uniform distribution + MMax upper range of uniform distribution +Globals: None +Operation: This routine computes a random number which comes from a + uniform distribution over the range from MMin to MMax. +Return: Uniform random number +Exceptions: None +History: 6/6/89, DSJ, Created. +*******************************************************************************/ +FLOAT32 UniformRandomNumber(FLOAT32 MMin, FLOAT32 MMax) { + double fake_drand48(); + FLOAT32 RandomNumber; + + RandomNumber = fake_drand48 (); + return (MMin + (RandomNumber * (MMax - MMin))); +} // UniformRandomNumber + + +/** drand48 ************************************************************* +Cheap replacement for drand48 which is not available on the PC. +**********************************************************************/ + +double fake_drand48() { + return rand () / (RAND_MAX + 1.0); +} diff --git a/classify/clusttool.h b/classify/clusttool.h new file mode 100644 index 0000000000..aa55d9efe4 --- /dev/null +++ b/classify/clusttool.h @@ -0,0 +1,70 @@ +/****************************************************************************** + ** Filename: clusttool.h + ** Purpose: Definition of clustering utility tools + ** Author: Dan Johnson + ** History: 6/6/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef __CLUSTERTOOL__ +#define __CLUSTERTOOL__ + +//--------------------------Include Files--------------------------------------- +#include "host.h" +#include "cluster.h" +#include + +/*------------------------------------------------------------------------- + Public Funtion Prototype +--------------------------------------------------------------------------*/ +UINT16 ReadSampleSize(FILE *File); + +PARAM_DESC *ReadParamDesc(FILE *File, UINT16 N); + +PROTOTYPE *ReadPrototype(FILE *File, UINT16 N); + +PROTOSTYLE ReadProtoStyle(FILE *File); + +FLOAT32 *ReadNFloats (FILE * File, UINT16 N, FLOAT32 Buffer[]); + +void WriteParamDesc (FILE * File, UINT16 N, PARAM_DESC ParamDesc[]); + +void WritePrototype(FILE *File, UINT16 N, PROTOTYPE *Proto); + +void WriteNFloats (FILE * File, UINT16 N, FLOAT32 Array[]); + +void WriteProtoStyle(FILE *File, PROTOSTYLE ProtoStyle); + +void WriteProtoList( + FILE *File, + UINT16 N, + PARAM_DESC ParamDesc[], + LIST ProtoList, + BOOL8 WriteSigProtos, + BOOL8 WriteInsigProtos); + +FLOAT32 UniformRandomNumber(FLOAT32 MMin, FLOAT32 MMax); + +//--------------Global Data Definitions and Declarations--------------------- +// define errors that can be trapped +#define ILLEGALSAMPLESIZE 5000 +#define ILLEGALCIRCULARSPEC 5001 +#define ILLEGALMINMAXSPEC 5002 +#define ILLEGALSIGNIFICANCESPEC 5003 +#define ILLEGALSTYLESPEC 5004 +#define ILLEGALSAMPLECOUNT 5005 +#define ILLEGALMEANSPEC 5006 +#define ILLEGALVARIANCESPEC 5007 +#define ILLEGALDISTRIBUTION 5008 +#define ILLEGALFLOAT 5009 +#define ILLEGALESSENTIALSPEC 5013 +#endif diff --git a/classify/cutoffs.cpp b/classify/cutoffs.cpp new file mode 100644 index 0000000000..b76d55de14 --- /dev/null +++ b/classify/cutoffs.cpp @@ -0,0 +1,67 @@ +/****************************************************************************** + ** Filename: cutoffs.c + ** Purpose: Routines to manipulate an array of class cutoffs. + ** Author: Dan Johnson + ** History: Wed Feb 20 09:28:51 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "cutoffs.h" +#include "efio.h" +#include "scanutils.h" +#include + +#define MAX_CUTOFF 1000 + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ReadNewCutoffs(char *Filename, + CLASS_TO_INDEX ClassMapper, + CLASS_CUTOFF_ARRAY Cutoffs) { +/* + ** Parameters: + ** Filename name of file containing cutoff definitions + ** ClassMapper array which maps class id's to class indexes + ** Cutoffs array to put cutoffs into + ** Globals: none + ** Operation: Open Filename, read in all of the class-id/cutoff pairs + ** and insert them into the Cutoffs array. Cutoffs are + ** inserted in the array so that the array is indexed by + ** class index rather than class id. Unused entries in the + ** array are set to an arbitrarily high cutoff value. + ** Return: none + ** Exceptions: none + ** History: Wed Feb 20 09:38:26 1991, DSJ, Created. + */ + FILE *CutoffFile; + char Class[2]; + CLASS_ID ClassId; + int Cutoff; + int i; + + CutoffFile = Efopen (Filename, "r"); + + for (i = 0; i < MAX_NUM_CLASSES; i++) + Cutoffs[i] = MAX_CUTOFF; + + while (fscanf (CutoffFile, "%1s %d", Class, &Cutoff) == 2) { + ClassId = Class[0]; + Cutoffs[ClassMapper[ClassId]] = Cutoff; + } + fclose(CutoffFile); + +} /* ReadNewCutoffs */ diff --git a/classify/cutoffs.h b/classify/cutoffs.h new file mode 100644 index 0000000000..49b46015e8 --- /dev/null +++ b/classify/cutoffs.h @@ -0,0 +1,49 @@ +/****************************************************************************** + ** Filename: cutoffs.h + ** Purpose: Routines to manipulate an array of class cutoffs. + ** Author: Dan Johnson + ** History: Wed Feb 20 09:34:20 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef CUTOFFS_H +#define CUTOFFS_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "matchdefs.h" + +typedef UINT16 CLASS_CUTOFF_ARRAY[MAX_NUM_CLASSES]; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void ReadNewCutoffs(char *Filename, + CLASS_TO_INDEX ClassMapper, + CLASS_CUTOFF_ARRAY Cutoffs); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* cutoffs.c +void ReadNewCutoffs + _ARGS((char *Filename, + CLASS_TO_INDEX ClassMapper, + CLASS_CUTOFF_ARRAY Cutoffs)); +#undef _ARGS +*/ +#endif diff --git a/classify/extern.h b/classify/extern.h new file mode 100644 index 0000000000..cfae28c72a --- /dev/null +++ b/classify/extern.h @@ -0,0 +1,35 @@ +#ifndef EXTERN_H +#define EXTERN_H + +/* -*-C-*- + ******************************************************************************** + * + * File: extern.h (Formerly extern.h) + * Description: External definitions for C or C++ + * Author: Mark Seaman, OCR Technology + * Created: Tue Mar 20 14:01:22 1990 + * Modified: Tue Mar 20 14:02:09 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + */ + +#ifdef __cplusplus +#define EXTERN extern "C" +#else +#define EXTERN extern +#endif +#endif diff --git a/classify/extract.cpp b/classify/extract.cpp new file mode 100644 index 0000000000..74b34eb139 --- /dev/null +++ b/classify/extract.cpp @@ -0,0 +1,100 @@ +/****************************************************************************** + ** Filename: extract.c + ** Purpose: Generic high level feature extractor routines. + ** Author: Dan Johnson + ** History: Sun Jan 21 09:44:08 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "extract.h" +#include "variables.h" +#include "flexfx.h" +#include "funcdefs.h" +#include "danerror.h" + +typedef CHAR_FEATURES (*CF_FUNC) (); + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +void ExtractorStub(); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* tables to keep track of the different low level feature extractors */ +#define NUM_FX 3 +#define DEFAULT_FX 2 + +int CurrentFx = DEFAULT_FX; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +CHAR_DESC ExtractBlobFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract features from + ** LineStats statistics about line blob is in + ** Operation: Extract features from Blob by calling the feature + ** extractor which is currently being used. This routine + ** simply provides a high level interface to feature + ** extraction. The caller can extract any type of features + ** from a blob without understanding any lower level details. + ** Return: The character features extracted from Blob. + ** Exceptions: none + ** History: Sun Jan 21 10:07:28 1990, DSJ, Created. + */ + return (ExtractFlexFeatures (Blob, LineStats)); +} /* ExtractBlobFeatures */ + + +/*---------------------------------------------------------------------------*/ +void InitExtractorVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Install global extractor variables into the variable + ** system. + ** Return: none + ** Exceptions: none + ** History: Sun Jan 21 10:19:59 1990, DSJ, Created. + */ + // VALUE dummy; + InitFlexFXVars(); + +} /* InitExtractorVars */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void +ExtractorStub () +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine is used to stub out feature extractors + ** that are no longer used. It simply calls DoError. + ** Return: none + ** Exceptions: none + ** History: Wed Jan 2 14:16:49 1991, DSJ, Created. + */ +#define DUMMY_ERROR 1 +{ + DoError (DUMMY_ERROR, "Selected feature extractor has been stubbed out!"); +} /* ExtractorStub */ diff --git a/classify/extract.h b/classify/extract.h new file mode 100644 index 0000000000..7a16e91796 --- /dev/null +++ b/classify/extract.h @@ -0,0 +1,36 @@ +/****************************************************************************** + ** Filename: extract.h + ** Purpose: Interface to high level generic feature extraction. + ** Author: Dan Johnson + ** History: 1/21/90, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef EXTRACT_H +#define EXTRACT_H + +#include "fxdefs.h" +#include "featdefs.h" +#include + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +CHAR_DESC ExtractBlobFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +void InitExtractorVars(); + +/*--------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------*/ +void ExtractorStub(); +#endif diff --git a/classify/featdefs.cpp b/classify/featdefs.cpp new file mode 100644 index 0000000000..e162c4fa79 --- /dev/null +++ b/classify/featdefs.cpp @@ -0,0 +1,244 @@ +/****************************************************************************** + ** Filename: featdefs.c + ** Purpose: Definitions of currently defined feature types. + ** Author: Dan Johnson + ** History: Mon May 21 10:26:21 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "featdefs.h" +#include "emalloc.h" +#include "danerror.h" +#include "scanutils.h" +#include "variables.h" +#include "sigmenu.h" + +#include +#include + +/* define errors triggered by this module */ +#define ILLEGAL_NUM_SETS 3001 + +#define PICO_FEATURE_LENGTH 0.05 +#define MAX_OUTLINE_FEATURES 100 + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* define all of the parameters for the MicroFeature type*/ +StartParamDesc (MicroFeatureParams) +DefineParam (0, 0, -0.5, 0.5) +DefineParam (0, 0, -0.25, 0.75) +DefineParam (0, 0, 0.0, 1.0) +DefineParam (1, 0, 0.0, 1.0) +DefineParam (0, 1, -0.5, 0.5) +DefineParam (0, 1, -0.5, 0.5) +EndParamDesc +/* now define the feature type itself (see features.h for info about each + parameter).*/ +DefineFeature (MicroFeatureDesc, 5, 1, 4, 50, "Micro", "mf", MicroFeatureParams) + +// define all of the parameters for the PicoFeature type +/* define knob that can be used to adjust pico-feature length */ +FLOAT32 PicoFeatureLength = PICO_FEATURE_LENGTH; +StartParamDesc (PicoFeatParams) +DefineParam (0, 0, -0.25, 0.75) +DefineParam (1, 0, 0.0, 1.0) +DefineParam (0, 0, -0.5, 0.5) +EndParamDesc +/* now define the feature type itself (see features.h for info about each + parameter).*/ +DefineFeature (PicoFeatDesc, 2, 1, 1, MAX_UINT8, "Pico", "pf", PicoFeatParams) + +/* define all of the parameters for the NormFeat type*/ +StartParamDesc (CharNormParams) +DefineParam (0, 0, -0.25, 0.75) +DefineParam (0, 0, 0.0, 1.0) +DefineParam (0, 0, 0.0, 1.0) +DefineParam (0, 0, 0.0, 1.0) +EndParamDesc +/* now define the feature type itself (see features.h for info about each + parameter).*/ +DefineFeature (CharNormDesc, 4, 0, 1, 1, "CharNorm", "cn", CharNormParams) + +// define all of the parameters for the OutlineFeature type +StartParamDesc (OutlineFeatParams) +DefineParam (0, 0, -0.5, 0.5) +DefineParam (0, 0, -0.25, 0.75) +DefineParam (0, 0, 0.0, 1.0) +DefineParam (1, 0, 0.0, 1.0) +EndParamDesc +/* now define the feature type itself (see features.h for info about each + parameter).*/ +DefineFeature (OutlineFeatDesc, 3, 1, 1, MAX_OUTLINE_FEATURES, "Outline", + "of", OutlineFeatParams) + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +FEATURE_DEFS_STRUCT FeatureDefs = { + NUM_FEATURE_TYPES, + { + &MicroFeatureDesc, + &PicoFeatDesc, + &OutlineFeatDesc, + &CharNormDesc + } +}; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void FreeCharDescription(CHAR_DESC CharDesc) { +/* + ** Parameters: + ** CharDesc character description to be deallocated + ** Globals: none + ** Operation: Release the memory consumed by the specified character + ** description and all of the features in that description. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 13:52:19 1990, DSJ, Created. + */ + int i; + + if (CharDesc) { + for (i = 0; i < NumFeatureSetsIn (CharDesc); i++) + FreeFeatureSet (FeaturesOfType (CharDesc, i)); + Efree(CharDesc); + } +} /* FreeCharDescription */ + + +/*---------------------------------------------------------------------------*/ +CHAR_DESC NewCharDescription() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Allocate a new character description, initialize its + ** feature sets to be empty, and return it. + ** Return: New character description structure. + ** Exceptions: none + ** History: Wed May 23 15:27:10 1990, DSJ, Created. + */ + CHAR_DESC CharDesc; + int i; + + CharDesc = (CHAR_DESC) Emalloc (sizeof (CHAR_DESC_STRUCT)); + NumFeatureSetsIn (CharDesc) = NumFeaturesDefined (); + + for (i = 0; i < NumFeatureSetsIn (CharDesc); i++) + FeaturesOfType (CharDesc, i) = NULL; + + return (CharDesc); + +} /* NewCharDescription */ + + +/*---------------------------------------------------------------------------*/ +void WriteCharDescription(FILE *File, CHAR_DESC CharDesc) { +/* + ** Parameters: + ** File open text file to write CharDesc to + ** CharDesc character description to write to File + ** Globals: none + ** Operation: Write a textual representation of CharDesc to File. + ** The format used is to write out the number of feature + ** sets which will be written followed by a representation of + ** each feature set. + ** Each set starts with the short name for that feature followed + ** by a description of the feature set. Feature sets which are + ** not present are not written. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 17:21:18 1990, DSJ, Created. + */ + int Type; + int NumSetsToWrite = 0; + + for (Type = 0; Type < NumFeatureSetsIn (CharDesc); Type++) + if (FeaturesOfType (CharDesc, Type)) + NumSetsToWrite++; + + fprintf (File, " %d\n", NumSetsToWrite); + for (Type = 0; Type < NumFeatureSetsIn (CharDesc); Type++) + if (FeaturesOfType (CharDesc, Type)) { + fprintf (File, "%s ", ShortNameOf (DefinitionOf (Type))); + WriteFeatureSet (File, FeaturesOfType (CharDesc, Type)); + } +} /* WriteCharDescription */ + + +/*---------------------------------------------------------------------------*/ +CHAR_DESC ReadCharDescription(FILE *File) { +/* + ** Parameters: + ** File open text file to read character description from + ** Globals: none + ** Operation: Read a character description from File, and return + ** a data structure containing this information. The data + ** is formatted as follows: + ** NumberOfSets + ** ShortNameForSet1 Set1 + ** ShortNameForSet2 Set2 + ** ... + ** Return: Character description read from File. + ** Exceptions: ILLEGAL_NUM_SETS + ** History: Wed May 23 17:32:48 1990, DSJ, Created. + */ + int NumSetsToRead; + char ShortName[FEAT_NAME_SIZE]; + CHAR_DESC CharDesc; + int Type; + + if (fscanf (File, "%d", &NumSetsToRead) != 1 || + NumSetsToRead < 0 || NumSetsToRead > NumFeaturesDefined ()) + DoError (ILLEGAL_NUM_SETS, "Illegal number of feature sets"); + + CharDesc = NewCharDescription (); + for (; NumSetsToRead > 0; NumSetsToRead--) { + fscanf (File, "%s", ShortName); + Type = ShortNameToFeatureType (ShortName); + FeaturesOfType (CharDesc, Type) = + ReadFeatureSet (File, DefinitionOf (Type)); + } + return (CharDesc); + +} // ReadCharDescription + + +/*---------------------------------------------------------------------------*/ +int ShortNameToFeatureType(const char *ShortName) { +/* + ** Parameters: + ** ShortName short name of a feature type + ** Globals: none + ** Operation: Search thru all features currently defined and return + ** the feature type for the feature with the specified short + ** name. Trap an error if the specified name is not found. + ** Return: Feature type which corresponds to ShortName. + ** Exceptions: ILLEGAL_SHORT_NAME + ** History: Wed May 23 15:36:05 1990, DSJ, Created. + */ + int i; + + for (i = 0; i < NumFeaturesDefined (); i++) + if (!strcmp (ShortNameOf (DefinitionOf (i)), ShortName)) + return (i); + DoError (ILLEGAL_SHORT_NAME, "Illegal short name for a feature"); + return 0; + +} // ShortNameToFeatureType diff --git a/classify/featdefs.h b/classify/featdefs.h new file mode 100644 index 0000000000..4610ebb315 --- /dev/null +++ b/classify/featdefs.h @@ -0,0 +1,85 @@ +/****************************************************************************** + ** Filename: featdefs.h + ** Purpose: Definitions of currently defined feature types. + ** Author: Dan Johnson + ** History: Mon May 21 08:28:01 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef FEATDEFS_H +#define FEATDEFS_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "ocrfeatures.h" + +/* Enumerate the different types of features currently defined. */ +#define NUM_FEATURE_TYPES 4 + +/* define error traps which can be triggered by this module.*/ +#define ILLEGAL_SHORT_NAME 2000 + +/* A character is described by multiple sets of extracted features. Each + set contains a number of features of a particular type, for example, a + set of bays, or a set of closures, or a set of microfeatures. Each + feature consists of a number of parameters. All features within a + feature set contain the same number of parameters.*/ + +typedef struct +{ + UINT32 NumFeatureSets; + FEATURE_SET FeatureSets[NUM_FEATURE_TYPES]; +} CHAR_DESC_STRUCT; +typedef CHAR_DESC_STRUCT *CHAR_DESC; + +typedef struct +{ + UINT32 NumFeatureTypes; + FEATURE_DESC FeatureDesc[NUM_FEATURE_TYPES]; + FEATURE_EXT_STRUCT* FeatureExtractors[NUM_FEATURE_TYPES]; + int FeatureEnabled[NUM_FEATURE_TYPES]; +} FEATURE_DEFS_STRUCT; +typedef FEATURE_DEFS_STRUCT *FEATURE_DEFS; + +/*---------------------------------------------------------------------- + Macros for finding feature definitions +----------------------------------------------------------------------*/ +#define NumFeaturesDefined() (FeatureDefs.NumFeatureTypes) +#define DefinitionOf(Type) (FeatureDefs.FeatureDesc[Type]) +#define ExtractorOf(Type) (FeatureDefs.FeatureExtractors[Type]) +#define FeatureOn(Type) (FeatureDefs.FeatureEnabled[Type]) + +/*---------------------------------------------------------------------- + Macros for manipulating character descriptions +----------------------------------------------------------------------*/ +#define NumFeatureSetsIn(Char) ((Char)->NumFeatureSets) +#define FeaturesOfType(Char, Type) ((Char)->FeatureSets[Type]) + +/*---------------------------------------------------------------------- + Generic functions for manipulating character descriptions +----------------------------------------------------------------------*/ +void FreeCharDescription(CHAR_DESC CharDesc); + +CHAR_DESC NewCharDescription(); + +void WriteCharDescription(FILE *File, CHAR_DESC CharDesc); + +CHAR_DESC ReadCharDescription(FILE *File); + +int ShortNameToFeatureType(const char *ShortName); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern FEATURE_DEFS_STRUCT FeatureDefs; +#endif diff --git a/classify/flexfx.cpp b/classify/flexfx.cpp new file mode 100644 index 0000000000..c9f79c57c5 --- /dev/null +++ b/classify/flexfx.cpp @@ -0,0 +1,86 @@ +/****************************************************************************** + ** Filename: flexfx.c + ** Purpose: Interface to flexible feature extractor. + ** Author: Dan Johnson + ** History: Wed May 23 13:45:10 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "flexfx.h" +#include "featdefs.h" +#include "variables.h" +#include "sigmenu.h" +#include "emalloc.h" +#include +#include + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +CHAR_DESC ExtractFlexFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract features from + ** LineStats statistics about text line Blob is on + ** Globals: none + ** Operation: Allocate a new character descriptor and fill it in by + ** calling all feature extractors which are enabled. + ** Return: Structure containing features extracted from Blob. + ** Exceptions: none + ** History: Wed May 23 13:46:22 1990, DSJ, Created. + */ + int Type; + CHAR_DESC CharDesc; + + CharDesc = NewCharDescription (); + + for (Type = 0; Type < NumFeatureSetsIn(CharDesc); Type++) + if (ExtractorOf(Type) != NULL && ExtractorOf(Type)->Extractor != NULL) + FeaturesOfType(CharDesc, Type) = + ExtractUsing(ExtractorOf(Type)) (Blob, LineStats); + + return (CharDesc); + +} /* ExtractFlexFeatures */ + + +/*---------------------------------------------------------------------------*/ +void +InitFlexFXVars () +/* + ** Parameters: none + ** Globals: none + ** Operation: Add any control variables used by the feature extractors + ** to the variable system. This includes the enable flag for + ** each individual extractor. This routine needs to create + ** a separate name for the enable for each feature extractor + ** and allocate a string to contain that name. This is + ** necessary since the "variables" routines do not create + ** copies of the string names passed to them. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 15:59:23 1990, DSJ, Created. + */ +#define NamePrefix "Enable" +#define NameSuffix "Features" +{ + int Type; + + SetupExtractors(); + for (Type = 0; Type < NumFeaturesDefined (); Type++) { + InitFXVarsUsing (ExtractorOf (Type)) (); + } +} /* InitFlexFXVars */ diff --git a/classify/flexfx.h b/classify/flexfx.h new file mode 100644 index 0000000000..d519466fb9 --- /dev/null +++ b/classify/flexfx.h @@ -0,0 +1,34 @@ +/****************************************************************************** + ** Filename: flexfx.h + ** Purpose: Interface to flexible feature extractor. + ** Author: Dan Johnson + ** History: Wed May 23 13:36:58 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef FLEXFX_H +#define FLEXFX_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "fxdefs.h" +#include "featdefs.h" +#include + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +CHAR_DESC ExtractFlexFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +void InitFlexFXVars(); +#endif diff --git a/classify/float2int.cpp b/classify/float2int.cpp new file mode 100644 index 0000000000..ddeac37ff0 --- /dev/null +++ b/classify/float2int.cpp @@ -0,0 +1,126 @@ +/****************************************************************************** + ** Filename: float2int.c + ** Purpose: Routines for converting float features to int features + ** Author: Dan Johnson + ** History: Wed Mar 13 07:47:48 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "float2int.h" +#include "normmatch.h" +#include "mfoutline.h" +#include "picofeat.h" + +#define MAX_INT_CHAR_NORM (INT_CHAR_NORM_RANGE - 1) + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ClearCharNormArray(INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray) { +/* + ** Parameters: + ** Templates specifies classes currently defined + ** CharNormArray array to be cleared + ** Globals: none + ** Operation: For each class in Templates, clear the corresponding + ** entry in CharNormArray. CharNormArray is indexed by class + ** indicies (as obtained from Templates) rather than class id's. + ** Return: none + ** Exceptions: none + ** History: Wed Feb 20 11:20:54 1991, DSJ, Created. + */ + int i; + + for (i = 0; i < NumClassesIn (Templates); i++) { + CharNormArray[i] = 0; + } + +} /* ClearCharNormArray */ + + +/*---------------------------------------------------------------------------*/ +void ComputeIntCharNormArray(FEATURE NormFeature, + INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray) { +/* + ** Parameters: + ** NormFeature character normalization feature + ** Templates specifies classes currently defined + ** CharNormArray place to put results + ** Globals: none + ** Operation: For each class in Templates, compute the match between + ** NormFeature and the normalization protos for that class. + ** Convert this number to the range from 0 - 255 and store it + ** into CharNormArray. CharNormArray is indexed by class + ** indicies (as obtained from Templates) rather than class id's. + ** Return: none (results are returned in CharNormArray) + ** Exceptions: none + ** History: Wed Feb 20 11:20:54 1991, DSJ, Created. + */ + int i; + int NormAdjust; + + for (i = 0; i < NumClassesIn (Templates); i++) { + NormAdjust = (int) (INT_CHAR_NORM_RANGE * + ComputeNormMatch (ClassIdForIndex (Templates, i), + NormFeature, FALSE)); + if (NormAdjust < 0) + NormAdjust = 0; + else if (NormAdjust > MAX_INT_CHAR_NORM) + NormAdjust = MAX_INT_CHAR_NORM; + + CharNormArray[i] = NormAdjust; + } + +} /* ComputeIntCharNormArray */ + + +/*---------------------------------------------------------------------------*/ +void ComputeIntFeatures(FEATURE_SET Features, INT_FEATURE_ARRAY IntFeatures) { +/* + ** Parameters: + ** Features floating point pico-features to be converted + ** IntFeatures array to put converted features into + ** Globals: none + ** Operation: This routine converts each floating point pico-feature + ** in Features into integer format and saves it into + ** IntFeatures. + ** Return: none (results are returned in IntFeatures) + ** Exceptions: none + ** History: Wed Feb 20 10:58:45 1991, DSJ, Created. + */ + int Fid; + FEATURE Feature; + FLOAT32 YShift; + + if (NormMethod == baseline) + YShift = BASELINE_Y_SHIFT; + else + YShift = Y_SHIFT; + + for (Fid = 0; Fid < NumFeaturesIn (Features); Fid++) { + Feature = FeatureIn (Features, Fid); + + IntFeatures[Fid].X = BucketFor (ParamOf (Feature, PicoFeatX), + X_SHIFT, INT_FEAT_RANGE); + IntFeatures[Fid].Y = BucketFor (ParamOf (Feature, PicoFeatY), + YShift, INT_FEAT_RANGE); + IntFeatures[Fid].Theta = CircBucketFor (ParamOf (Feature, PicoFeatDir), + ANGLE_SHIFT, INT_FEAT_RANGE); + IntFeatures[Fid].CP_misses = 0; + } +} /* ComputeIntFeatures */ diff --git a/classify/float2int.h b/classify/float2int.h new file mode 100644 index 0000000000..60d1dead3f --- /dev/null +++ b/classify/float2int.h @@ -0,0 +1,65 @@ +/****************************************************************************** + ** Filename: float2int.h + ** Purpose: Routines for converting float features to int features + ** Author: Dan Johnson + ** History: Wed Mar 13 08:06:41 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef FLOAT2INT_H +#define FLOAT2INT_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "intmatcher.h" +#include "ocrfeatures.h" + +#define INT_FEAT_RANGE 256 +#define BASELINE_Y_SHIFT (0.25) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void ClearCharNormArray(INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray); + +void ComputeIntCharNormArray(FEATURE NormFeature, + INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray); + +void ComputeIntFeatures(FEATURE_SET Features, INT_FEATURE_ARRAY IntFeatures); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* float2int.c +void ClearCharNormArray + _ARGS((INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray)); + +void ComputeIntCharNormArray + _ARGS((FEATURE NormFeature, + INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray)); + +void ComputeIntFeatures + _ARGS((FEATURE_SET Features, + INT_FEATURE_ARRAY IntFeatures)); + +#undef _ARGS +*/ +#endif diff --git a/classify/fpoint.cpp b/classify/fpoint.cpp new file mode 100644 index 0000000000..763c662608 --- /dev/null +++ b/classify/fpoint.cpp @@ -0,0 +1,56 @@ +/****************************************************************************** + ** Filename: fpoint.c + ** Purpose: Abstract data type for a 2D point (floating point coords) + ** Author: Dan Johnson + ** History: Thu Apr 12 10:44:15 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "const.h" +#include "fpoint.h" +#include +#include + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FLOAT32 NormalizedAngleFrom(FPOINT *Point1, + FPOINT *Point2, + FLOAT32 FullScale) { +/* + ** Parameters: + ** Point1, Point2 points to compute angle between + ** FullScale value to associate with 2*pi + ** Globals: none + ** Operation: Return the angle from Point1 to Point2 normalized to + ** lie in the range 0 to FullScale (where FullScale corresponds + ** to 2*pi or 360 degrees). + ** Return: none + ** Exceptions: none + ** History: Wed Mar 28 14:27:25 1990, DSJ, Created. + */ + FLOAT32 Angle; + FLOAT32 NumRadsInCircle = 2.0 * PI; + + Angle = AngleFrom (*Point1, *Point2); + if (Angle < 0.0) + Angle += NumRadsInCircle; + Angle *= FullScale / NumRadsInCircle; + if (Angle < 0.0 || Angle >= FullScale) + Angle = 0.0; + return (Angle); + +} /* NormalizedAngleFrom */ diff --git a/classify/fpoint.h b/classify/fpoint.h new file mode 100644 index 0000000000..74c16e4469 --- /dev/null +++ b/classify/fpoint.h @@ -0,0 +1,62 @@ +/****************************************************************************** + ** Filename: fpoint.h + ** Purpose: Abstract data type for 2D points (floating point coords) + ** Author: Dan Johnson + ** History: Thu Apr 12 10:50:01 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef FPOINT_H +#define FPOINT_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "general.h" +#include +#include + +/* define data structure to hold 2D points or vectors using floating point */ +typedef struct +{ + FLOAT32 x, y; +} FPOINT; +typedef FPOINT FVECTOR; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* macros for manipulating simple point data structures */ +#define Xof(P) ( (P).x ) +#define Yof(P) ( (P).y ) +#define XofP(P) ((P)->x) +#define YofP(P) ((P)->y) +#define FillPoint(P,X,Y) ( Xof(P) = (X), Yof(P) = (Y) ) +#define CopyPoint(A,B) ( Xof(B) = Xof(A), Yof(B) = Yof(A) ) + +/* macros for computing miscellaneous functions of 2 points */ +#define XDelta(A,B) ( Xof(B) - Xof(A) ) +#define YDelta(A,B) ( Yof(B) - Yof(A) ) +#define DistanceBetween(A,B) \ +(sqrt ((double) (XDelta(A,B) * XDelta(A,B) + YDelta(A,B) * YDelta(A,B)))) + +#define SlopeFrom(A,B) ( YDelta(A,B) / XDelta(A,B) ) +#define AngleFrom(A,B) ( atan2((double) YDelta(A,B), \ + (double) XDelta(A,B) ) ) + +#define XIntersectionOf(A,B,X) ( SlopeFrom(A,B) * ((X) - Xof(A)) + Yof(A)) + +/*------------------------------------------------------------------------- + Public Function Prototypes +---------------------------------------------------------------------------*/ +FLOAT32 NormalizedAngleFrom(FPOINT *Point1, FPOINT *Point2, FLOAT32 FullScale); +#endif diff --git a/classify/fxdefs.cpp b/classify/fxdefs.cpp new file mode 100644 index 0000000000..cb1960b2cd --- /dev/null +++ b/classify/fxdefs.cpp @@ -0,0 +1,74 @@ +/****************************************************************************** + ** Filename: fxdefs.c + ** Purpose: Utility functions to be used by feature extractors. + ** Author: Dan Johnson + ** History: Sun Jan 21 15:29:02 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#include "fxdefs.h" +#include "featdefs.h" +#include "mf.h" +#include "outfeat.h" +#include "picofeat.h" +#include "normfeat.h" + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* flag to control learn mode vs. classify mode */ +int ExtractMode; + +// Definitions of extractors separated from feature definitions. +DefineFeatureExt (MicroFeatureExt, ExtractMicros, InitMicroFXVars) +DefineFeatureExt (PicoFeatExt, NULL, DefaultInitFXVars) +DefineFeatureExt (CharNormExt, ExtractCharNormFeatures, DefaultInitFXVars) +DefineFeatureExt (OutlineFeatExt, NULL, DefaultInitFXVars) + +FEATURE_EXT_STRUCT* ExtractorDefs[NUM_FEATURE_TYPES] = { + &MicroFeatureExt, + &PicoFeatExt, + &OutlineFeatExt, + &CharNormExt +}; + + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void SetupExtractors() { + for (int i = 0; i < NUM_FEATURE_TYPES; ++i) + ExtractorOf(i) = ExtractorDefs[i]; +} + +void GetLineStatsFromRow(TEXTROW *Row, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Row text row to get line statistics from + ** LineStats place to put line statistics + ** Globals: none + ** Operation: This routine copies the relavent fields from the + ** Row struct to the LineStats struct. + ** Return: none (results are returned in LineStats) + ** Exceptions: none + ** History: Mon Mar 11 10:38:43 1991, DSJ, Created. + */ + LineStats->Baseline = &(Row->baseline); + LineStats->XHeightLine = &(Row->xheight); + LineStats->xheight = Row->lineheight; + LineStats->AscRise = Row->ascrise; + LineStats->DescDrop = Row->descdrop; + LineStats->TextRow = Row; /* kludge - only needed by fx for */ + /* fast matcher - remove later */ + +} /* GetLineStatsFromRow */ diff --git a/classify/fxdefs.h b/classify/fxdefs.h new file mode 100644 index 0000000000..5b6fd47d93 --- /dev/null +++ b/classify/fxdefs.h @@ -0,0 +1,93 @@ +/****************************************************************************** + ** Filename: fxdefs.h + ** Purpose: Generic interface definitions for feature extractors + ** Author: Dan Johnson + ** History: Fri Jan 19 09:04:14 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef FXDEFS_H +#define FXDEFS_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "tessclas.h" +#include "general.h" + +/* define different modes for feature extractor - learning vs. classifying */ +#define LEARN_MODE 0 +#define CLASSIFY_MODE 1 + +/* define a data structure to hold line statistics. These line statistics + are used to normalize character outlines to a standard size and position + relative to the baseline of the text. */ +typedef struct +{ + SPLINE_SPEC *Baseline; /* collection of splines describing baseline */ + SPLINE_SPEC *XHeightLine; /* collection of splines describing x-height */ + FLOAT32 xheight; /* avg. distance from x-height to baseline */ + FLOAT32 AscRise; /* avg. distance from ascenders to x-height */ + FLOAT32 DescDrop; /* avg. distance from baseline to descenders */ + /* always a negative number */ + TEXTROW *TextRow; /* kludge - only needed by fx for fast matcher */ + /* should be removed later */ +} + + +LINE_STATS; + +/* define a generic character description as a char pointer. In reality, + it will be a pointer to some data structure. Paired feature + extractors/matchers need to agree on the data structure to be used, + however, the high level classifier does not need to know the details + of this data structure. */ +typedef char *CHAR_FEATURES; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* macro to change and monitor the mode of the feature extractor. + In general, learn mode smears features which would otherwise be discrete + in nature; classify mode does not.*/ +#define SetExtractMode(M) (ExtractMode = (M)) +#define EnterLearnMode (SetExtractMode (LEARN_MODE)) +#define EnterClassifyMode (SetExtractMode (CLASSIFY_MODE)) + +/*---------------------------------------------------------------------------- + Public Function Prototypes +-----------------------------------------------------------------------------*/ +void SetupExtractors(); + +void GetLineStatsFromRow(TEXTROW *Row, LINE_STATS *LineStats); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* fxdefs.c +void GetLineStatsFromRow + _ARGS((TEXTROW *Row, + LINE_STATS *LineStats)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* flag to control learn mode vs. classify mode */ +extern int ExtractMode; +#endif diff --git a/classify/fxid.h b/classify/fxid.h new file mode 100644 index 0000000000..9db710fa29 --- /dev/null +++ b/classify/fxid.h @@ -0,0 +1,69 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: fxid.h (Formerly fxid.h) + * Description: Feature extractor related includes + * Author: Mark Seaman, OCR Technology + * Created: Thu Oct 19 14:59:51 1989 + * Modified: Thu Jan 31 16:57:07 1991 (Dan Johnson) danj@hpgrlj + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + + *************************************************************************** + ********** A T T E N T I O N ******** + *************************************************************************** + +This module is divided into two sections the declarations for this module +(i.e. the function signatures) are listed in 'fxid.h'. The definitions +(i.e. the actual code and variables) are listed in 'fxid1.c' and 'fxid2.c'. +The appropriate piece of code you need for your application should be +included in your top level program file. + +*/ + +#ifndef FXID_H +#define FXID_H + +#include "extern.h" +#include "const.h" +#include "tessclas.h" +#include "oldlist.h" + +#define FEATURE_MATCHER 1 /* Use micro-features */ + +#define WO_UNSCALED 0 /*first square scaled fx */ +#define STATISTICAL_WO 1 /*new wo */ +#define MICRO_FEATURES 2 /*microfeature extractor */ +#define WO_SCALED 3 /*wiseowl scaled to baseline */ +#define MAX_FX 3 /*no of working fx-ers */ +#define NO_FUNCTION 0 /*special value for nothing */ + +/* This file contains declarations of the top-level feature +extractor functions as used by the Classify process*/ + +typedef LIST (*LISTFUNC) (); + +//extern FUNCPTR word_matchers[MAX_FX]; + +//extern LISTFUNC blob_matchers[MAX_FX]; + +//extern FUNCPTR feature_learners[MAX_FX]; + +extern char fx_ids[MAX_FX]; /*one-char ids */ + +extern char *fx_names[MAX_FX]; +#endif diff --git a/classify/hideedge.cpp b/classify/hideedge.cpp new file mode 100644 index 0000000000..49367dd957 --- /dev/null +++ b/classify/hideedge.cpp @@ -0,0 +1,35 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: hideedge.c (Formerly hideedge.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Apr 30 10:38:29 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + */ + +/* +---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------- +*/ + +/*#include "stdafx.h"*/ +#include "hideedge.h" +#include "debug.h" diff --git a/classify/hideedge.h b/classify/hideedge.h new file mode 100644 index 0000000000..cf9ed6c21d --- /dev/null +++ b/classify/hideedge.h @@ -0,0 +1,76 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: hideedge.h (Formerly hideedge.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Apr 30 12:49:57 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + */ + +#ifndef HIDEEDGE_H +#define HIDEEDGE_H + +/* +---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------- +*/ + +#include "general.h" + +/* +---------------------------------------------------------------------- + M a c r o s +---------------------------------------------------------------------- +*/ + +/********************************************************************** + * is_hidden_edge + * + * Check to see if this edge is a hidden edge. This will prohibit + * feature extraction and display functions on this edge. The + * argument should be of type (EDGEPT*). + **********************************************************************/ + +#define is_hidden_edge(edge) \ +/*(hidden_edges &&*/ edge->flags[0] /*) */ + +/********************************************************************** + * hide_edge + * + * Make this edge a hidden edge. This will prohibit feature extraction + * and display functions on this edge. The argument should be of type + * (EDGEPT*). + **********************************************************************/ + +#define hide_edge(edge) \ +/*if (hidden_edges)*/ edge->flags[0] = TRUE + +/********************************************************************** + * reveal_edge + * + * Make this edge a unhidden edge. This will prohibit feature extraction + * and display functions on this edge. The argument should be of type + * (EDGEPT*). + **********************************************************************/ + +#define reveal_edge(edge) \ +/*if (hidden_edges)*/ edge->flags[0] = FALSE +#endif diff --git a/classify/intfx.cpp b/classify/intfx.cpp new file mode 100644 index 0000000000..d54c8c82aa --- /dev/null +++ b/classify/intfx.cpp @@ -0,0 +1,608 @@ +/****************************************************************************** + ** Filename: intfx.c + ** Purpose: Integer character normalization & feature extraction + ** Author: Robert Moss + ** History: Tue May 21 15:51:57 MDT 1991, RWM, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "intfx.h" +#include "intmatcher.h" +#include "const.h" +#ifdef __UNIX__ +#include +#endif + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +int SaveFeature(); +UINT8 TableLookup(); +UINT8 MySqrt2(); +void ClipRadius(); + +make_int_var (RadiusGyrMinMan, 255, MakeRadiusGyrMinMan, +16, 10, SetRadiusGyrMinMan, +"Minimum Radius of Gyration Mantissa 0-255: "); + +make_int_var (RadiusGyrMinExp, 0, MakeRadiusGyrMinExp, +16, 11, SetRadiusGyrMinExp, +"Minimum Radius of Gyration Exponent 0-255: "); + +make_int_var (RadiusGyrMaxMan, 158, MakeRadiusGyrMaxMan, +16, 12, SetRadiusGyrMaxMan, +"Maximum Radius of Gyration Mantissa 0-255: "); + +make_int_var (RadiusGyrMaxExp, 8, MakeRadiusGyrMaxExp, +16, 13, SetRadiusGyrMaxExp, +"Maximum Radius of Gyration Exponent 0-255: "); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#define ATAN_TABLE_SIZE 64 + +static UINT8 AtanTable[ATAN_TABLE_SIZE]; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void InitIntegerFX() { + int i; + + for (i = 0; i < ATAN_TABLE_SIZE; i++) + AtanTable[i] = + (UINT8) (atan ((i / (float) ATAN_TABLE_SIZE)) * 128.0 / PI + 0.5); + +} + + +/*--------------------------------------------------------------------------*/ +int ExtractIntFeat(TBLOB *Blob, + INT_FEATURE_ARRAY BLFeat, + INT_FEATURE_ARRAY CNFeat, + INT_FX_RESULT Results) { + + TESSLINE *OutLine; + EDGEPT *Loop, *LoopStart, *Segment; + INT16 LastX, LastY, Xmean, Ymean; + INT32 NormX, NormY, DeltaX, DeltaY; + INT32 Xsum, Ysum; + UINT32 Ix, Iy, LengthSum; + UINT16 n; + UINT8 Theta; + UINT16 NumBLFeatures, NumCNFeatures; + UINT8 RxInv, RyInv; /* x.xxxxxxx * 2^Exp */ + UINT8 RxExp, RyExp; + /* sxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxx */ + register INT32 pfX, pfY, dX, dY; + UINT16 Length; + register int i; + + Results->Length = 0; + Results->Xmean = 0; + Results->Ymean = 0; + Results->Rx = 0; + Results->Ry = 0; + Results->NumBL = 0; + Results->NumCN = 0; + + /* find Xmean, Ymean */ + NumBLFeatures = 0; + NumCNFeatures = 0; + OutLine = Blob->outlines; + Xsum = 0; + Ysum = 0; + LengthSum = 0; + while (OutLine != NULL) { + LoopStart = OutLine->loop; + Loop = LoopStart; + LastX = Loop->pos.x; + LastY = Loop->pos.y; + /* Check for bad loops */ + if ((Loop == NULL) || (Loop->next == NULL) || (Loop->next == LoopStart)) + return FALSE; + do { + Segment = Loop; + Loop = Loop->next; + NormX = Loop->pos.x; + NormY = Loop->pos.y; + + n = 1; + if (!is_hidden_edge (Segment)) { + DeltaX = NormX - LastX; + DeltaY = NormY - LastY; + Length = MySqrt (DeltaX, DeltaY); + n = ((Length << 2) + Length + 32) >> 6; + if (n != 0) { + Xsum += ((LastX << 1) + DeltaX) * (int) Length; + Ysum += ((LastY << 1) + DeltaY) * (int) Length; + LengthSum += Length; + } + } + if (n != 0) { /* Throw away a point that is too close */ + LastX = NormX; + LastY = NormY; + } + } + while (Loop != LoopStart); + OutLine = OutLine->next; + } + if (LengthSum == 0) + return FALSE; + Xmean = (Xsum / (INT32) LengthSum) >> 1; + Ymean = (Ysum / (INT32) LengthSum) >> 1; + + Results->Length = LengthSum; + Results->Xmean = Xmean; + Results->Ymean = Ymean; + + /* extract Baseline normalized features, */ + /* and find 2nd moments & radius of gyration */ + Ix = 0; + Iy = 0; + NumBLFeatures = 0; + OutLine = Blob->outlines; + while (OutLine != NULL) { + LoopStart = OutLine->loop; + Loop = LoopStart; + LastX = Loop->pos.x - Xmean; + LastY = Loop->pos.y; + /* Check for bad loops */ + if ((Loop == NULL) || (Loop->next == NULL) || (Loop->next == LoopStart)) + return FALSE; + do { + Segment = Loop; + Loop = Loop->next; + NormX = Loop->pos.x - Xmean; + NormY = Loop->pos.y; + + n = 1; + if (!is_hidden_edge (Segment)) { + DeltaX = NormX - LastX; + DeltaY = NormY - LastY; + Length = MySqrt (DeltaX, DeltaY); + n = ((Length << 2) + Length + 32) >> 6; + if (n != 0) { + Theta = TableLookup (DeltaY, DeltaX); + dX = (DeltaX << 8) / n; + dY = (DeltaY << 8) / n; + pfX = (LastX << 8) + (dX >> 1); + pfY = (LastY << 8) + (dY >> 1); + Ix += ((pfY >> 8) - Ymean) * ((pfY >> 8) - Ymean); + Iy += (pfX >> 8) * (pfX >> 8); + if (SaveFeature (BLFeat, NumBLFeatures, (INT16) (pfX >> 8), + (INT16) ((pfY >> 8) - 128), + Theta) == FALSE) + return FALSE; + NumBLFeatures++; + for (i = 1; i < n; i++) { + pfX += dX; + pfY += dY; + Ix += ((pfY >> 8) - Ymean) * ((pfY >> 8) - Ymean); + Iy += (pfX >> 8) * (pfX >> 8); + if (SaveFeature + (BLFeat, NumBLFeatures, (INT16) (pfX >> 8), + (INT16) ((pfY >> 8) - 128), Theta) == FALSE) + return FALSE; + NumBLFeatures++; + } + } + } + if (n != 0) { /* Throw away a point that is too close */ + LastX = NormX; + LastY = NormY; + } + } + while (Loop != LoopStart); + OutLine = OutLine->next; + } + if (Ix == 0) + Ix = 1; + if (Iy == 0) + Iy = 1; + RxInv = MySqrt2 (NumBLFeatures, Ix, &RxExp); + RyInv = MySqrt2 (NumBLFeatures, Iy, &RyExp); + ClipRadius(&RxInv, &RxExp, &RyInv, &RyExp); + + Results->Rx = (INT16) (51.2 / (double) RxInv * pow (2.0, (double) RxExp)); + Results->Ry = (INT16) (51.2 / (double) RyInv * pow (2.0, (double) RyExp)); + Results->NumBL = NumBLFeatures; + + /* extract character normalized features */ + NumCNFeatures = 0; + OutLine = Blob->outlines; + while (OutLine != NULL) { + LoopStart = OutLine->loop; + Loop = LoopStart; + LastX = (Loop->pos.x - Xmean) * RyInv; + LastY = (Loop->pos.y - Ymean) * RxInv; + LastX >>= (INT8) RyExp; + LastY >>= (INT8) RxExp; + /* Check for bad loops */ + if ((Loop == NULL) || (Loop->next == NULL) || (Loop->next == LoopStart)) + return FALSE; + do { + Segment = Loop; + Loop = Loop->next; + NormX = (Loop->pos.x - Xmean) * RyInv; + NormY = (Loop->pos.y - Ymean) * RxInv; + NormX >>= (INT8) RyExp; + NormY >>= (INT8) RxExp; + + n = 1; + if (!is_hidden_edge (Segment)) { + DeltaX = NormX - LastX; + DeltaY = NormY - LastY; + Length = MySqrt (DeltaX, DeltaY); + n = ((Length << 2) + Length + 32) >> 6; + if (n != 0) { + Theta = TableLookup (DeltaY, DeltaX); + dX = (DeltaX << 8) / n; + dY = (DeltaY << 8) / n; + pfX = (LastX << 8) + (dX >> 1); + pfY = (LastY << 8) + (dY >> 1); + if (SaveFeature (CNFeat, NumCNFeatures, (INT16) (pfX >> 8), + (INT16) ((pfY >> 8)), Theta) == FALSE) + return FALSE; + NumCNFeatures++; + for (i = 1; i < n; i++) { + pfX += dX; + pfY += dY; + if (SaveFeature + (CNFeat, NumCNFeatures, (INT16) (pfX >> 8), + (INT16) ((pfY >> 8)), Theta) == FALSE) + return FALSE; + NumCNFeatures++; + } + } + } + if (n != 0) { /* Throw away a point that is too close */ + LastX = NormX; + LastY = NormY; + } + } + while (Loop != LoopStart); + OutLine = OutLine->next; + } + + Results->NumCN = NumCNFeatures; + return TRUE; +} + + +/*--------------------------------------------------------------------------*/ +UINT8 TableLookup(INT32 Y, INT32 X) { + INT16 Angle; + UINT16 Ratio; + UINT32 AbsX, AbsY; + + assert ((X != 0) || (Y != 0)); + if (X < 0) + AbsX = -X; + else + AbsX = X; + if (Y < 0) + AbsY = -Y; + else + AbsY = Y; + if (AbsX > AbsY) + Ratio = AbsY * ATAN_TABLE_SIZE / AbsX; + else + Ratio = AbsX * ATAN_TABLE_SIZE / AbsY; + if (Ratio >= ATAN_TABLE_SIZE) + Ratio = ATAN_TABLE_SIZE - 1; + Angle = AtanTable[Ratio]; + if (X >= 0) + if (Y >= 0) + if (AbsX > AbsY) + Angle = Angle; + else + Angle = 64 - Angle; + else if (AbsX > AbsY) + Angle = 256 - Angle; + else + Angle = 192 + Angle; + else if (Y >= 0) + if (AbsX > AbsY) + Angle = 128 - Angle; + else + Angle = 64 + Angle; + else if (AbsX > AbsY) + Angle = 128 + Angle; + else + Angle = 192 - Angle; + + /* reverse angles to match old feature extractor: Angle += PI */ + Angle += 128; + Angle &= 255; + return (UINT8) Angle; +} + + +/*--------------------------------------------------------------------------*/ +int SaveFeature(INT_FEATURE_ARRAY FeatureArray, + UINT16 FeatureNum, + INT16 X, + INT16 Y, + UINT8 Theta) { + INT_FEATURE Feature; + + if (FeatureNum >= MAX_NUM_INT_FEATURES) + return FALSE; + + Feature = &(FeatureArray[FeatureNum]); + + X = X + 128; + Y = Y + 128; + + if (X > 255) + Feature->X = 255; + else if (X < 0) + Feature->X = 0; + else + Feature->X = X; + + if (Y > 255) + Feature->Y = 255; + else if (Y < 0) + Feature->Y = 0; + else + Feature->Y = Y; + + Feature->Theta = Theta; + + return TRUE; +} + + +/*---------------------------------------------------------------------------*/ +UINT16 MySqrt(INT32 X, INT32 Y) { + register UINT16 SqRoot; + register UINT32 Square; + register UINT16 BitLocation; + register UINT32 Sum; + + if (X < 0) + X = -X; + if (Y < 0) + Y = -Y; + + if (X > EvidenceMultMask) + X = EvidenceMultMask; + if (Y > EvidenceMultMask) + Y = EvidenceMultMask; + + Sum = X * X + Y * Y; + + BitLocation = 1024; + SqRoot = 0; + do { + Square = (SqRoot | BitLocation) * (SqRoot | BitLocation); + if (Square <= Sum) + SqRoot |= BitLocation; + BitLocation >>= 1; + } + while (BitLocation); + + return SqRoot; +} + + +/*--------------------------------------------------------------------------*/ +UINT8 MySqrt2(UINT16 N, UINT32 I, UINT8 *Exp) { + register INT8 k; + register UINT32 N2; + register UINT8 SqRoot; + register UINT16 Square; + register UINT8 BitLocation; + register UINT16 Ratio; + + N2 = N * 41943; + + k = 9; + while ((N2 & 0xc0000000) == 0) { + N2 <<= 2; + k += 1; + } + + while ((I & 0xc0000000) == 0) { + I <<= 2; + k -= 1; + } + + if (((N2 & 0x80000000) == 0) && ((I & 0x80000000) == 0)) { + N2 <<= 1; + I <<= 1; + } + + N2 &= 0xffff0000; + I >>= 14; + Ratio = N2 / I; + + BitLocation = 128; + SqRoot = 0; + do { + Square = (SqRoot | BitLocation) * (SqRoot | BitLocation); + if (Square <= Ratio) + SqRoot |= BitLocation; + BitLocation >>= 1; + } + while (BitLocation); + + if (k < 0) { + *Exp = 0; + return 255; + } + else { + *Exp = k; + return SqRoot; + } +} + + +/*-------------------------------------------------------------------------*/ +void ClipRadius(UINT8 *RxInv, UINT8 *RxExp, UINT8 *RyInv, UINT8 *RyExp) { + register UINT8 AM, BM, AE, BE; + register UINT8 BitN, LastCarry; + int RxInvLarge, RyInvSmall; + + AM = RadiusGyrMinMan; + AE = RadiusGyrMinExp; + BM = *RxInv; + BE = *RxExp; + LastCarry = 1; + while ((AM != 0) || (BM != 0)) { + if (AE > BE) { + BitN = LastCarry + (AM & 1) + 1; + AM >>= 1; + AE--; + } + else if (AE < BE) { + BitN = LastCarry + (!(BM & 1)); + BM >>= 1; + BE--; + } + else { /* AE == BE */ + BitN = LastCarry + (AM & 1) + (!(BM & 1)); + AM >>= 1; + BM >>= 1; + AE--; + BE--; + } + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + } + BitN = LastCarry + 1; + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + + if (BitN == 1) { + *RxInv = RadiusGyrMinMan; + *RxExp = RadiusGyrMinExp; + } + + AM = RadiusGyrMinMan; + AE = RadiusGyrMinExp; + BM = *RyInv; + BE = *RyExp; + LastCarry = 1; + while ((AM != 0) || (BM != 0)) { + if (AE > BE) { + BitN = LastCarry + (AM & 1) + 1; + AM >>= 1; + AE--; + } + else if (AE < BE) { + BitN = LastCarry + (!(BM & 1)); + BM >>= 1; + BE--; + } + else { /* AE == BE */ + BitN = LastCarry + (AM & 1) + (!(BM & 1)); + AM >>= 1; + BM >>= 1; + AE--; + BE--; + } + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + } + BitN = LastCarry + 1; + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + + if (BitN == 1) { + *RyInv = RadiusGyrMinMan; + *RyExp = RadiusGyrMinExp; + } + + AM = RadiusGyrMaxMan; + AE = RadiusGyrMaxExp; + BM = *RxInv; + BE = *RxExp; + LastCarry = 1; + while ((AM != 0) || (BM != 0)) { + if (AE > BE) { + BitN = LastCarry + (AM & 1) + 1; + AM >>= 1; + AE--; + } + else if (AE < BE) { + BitN = LastCarry + (!(BM & 1)); + BM >>= 1; + BE--; + } + else { /* AE == BE */ + BitN = LastCarry + (AM & 1) + (!(BM & 1)); + AM >>= 1; + BM >>= 1; + AE--; + BE--; + } + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + } + BitN = LastCarry + 1; + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + + if (BitN == 1) + RxInvLarge = 1; + else + RxInvLarge = 0; + + AM = *RyInv; + AE = *RyExp; + BM = RadiusGyrMaxMan; + BE = RadiusGyrMaxExp; + LastCarry = 1; + while ((AM != 0) || (BM != 0)) { + if (AE > BE) { + BitN = LastCarry + (AM & 1) + 1; + AM >>= 1; + AE--; + } + else if (AE < BE) { + BitN = LastCarry + (!(BM & 1)); + BM >>= 1; + BE--; + } + else { /* AE == BE */ + BitN = LastCarry + (AM & 1) + (!(BM & 1)); + AM >>= 1; + BM >>= 1; + AE--; + BE--; + } + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + } + BitN = LastCarry + 1; + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + + if (BitN == 1) + RyInvSmall = 1; + else + RyInvSmall = 0; + + if (RxInvLarge && RyInvSmall) { + *RyInv = RadiusGyrMaxMan; + *RyExp = RadiusGyrMaxExp; + } + +} diff --git a/classify/intfx.h b/classify/intfx.h new file mode 100644 index 0000000000..0cbab7d5b7 --- /dev/null +++ b/classify/intfx.h @@ -0,0 +1,63 @@ +/****************************************************************************** + ** Filename: intfx.h + ** Purpose: Interface to high level integer feature extractor. + ** Author: Robert Moss + ** History: Tue May 21 15:51:57 MDT 1991, RWM, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef INTFX_H +#define INTFX_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "tessclas.h" +#include "hideedge.h" +#include "intproto.h" +#include + +typedef struct +{ + INT32 Length; /* total length of all outlines */ + INT16 Xmean, Ymean; /* center of mass of all outlines */ + INT16 Rx, Ry; /* radius of gyration */ + INT16 NumBL, NumCN; /* number of features extracted */ +} + + +INT_FX_RESULT_STRUCT, *INT_FX_RESULT; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void InitIntegerFX(); + +int ExtractIntFeat(TBLOB *Blob, + INT_FEATURE_ARRAY BLFeat, + INT_FEATURE_ARRAY CNFeat, + INT_FX_RESULT Results); + +UINT8 TableLookup(INT32 Y, INT32 X); + +int SaveFeature(INT_FEATURE_ARRAY FeatureArray, + UINT16 FeatureNum, + INT16 X, + INT16 Y, + UINT8 Theta); + +UINT16 MySqrt(INT32 X, INT32 Y); + +UINT8 MySqrt2(UINT16 N, UINT32 I, UINT8 *Exp); + +void ClipRadius(UINT8 *RxInv, UINT8 *RxExp, UINT8 *RyInv, UINT8 *RyExp); +#endif diff --git a/classify/intmatcher.cpp b/classify/intmatcher.cpp new file mode 100644 index 0000000000..1626892982 --- /dev/null +++ b/classify/intmatcher.cpp @@ -0,0 +1,2321 @@ +/****************************************************************************** + ** Filename: intmatcher.c + ** Purpose: Generic high level classification routines. + ** Author: Robert Moss + ** History: Wed Feb 13 17:35:28 MST 1991, RWM, Created. + ** Mon Mar 11 16:33:02 MST 1991, RWM, Modified to add + ** support for adaptive matching. + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "intmatcher.h" +#include "tordvars.h" +#include "callcpp.h" +#include + +#define CLASS_MASK_SIZE ((MAX_NUM_CLASSES*NUM_BITS_PER_CLASS \ + +BITS_PER_WERD-1)/BITS_PER_WERD) + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#define SE_TABLE_BITS 9 +#define SE_TABLE_SIZE 512 +#define TEMPLATE_CACHE 2 +static UINT8 SimilarityEvidenceTable[SE_TABLE_SIZE]; +static UINT8 offset_table[256] = { + 255, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; +static UINT8 next_table[256] = { + 0, 0, 0, 0x2, 0, 0x4, 0x4, 0x6, 0, 0x8, 0x8, 0x0a, 0x08, 0x0c, 0x0c, 0x0e, + 0, 0x10, 0x10, 0x12, 0x10, 0x14, 0x14, 0x16, 0x10, 0x18, 0x18, 0x1a, 0x18, + 0x1c, 0x1c, 0x1e, + 0, 0x20, 0x20, 0x22, 0x20, 0x24, 0x24, 0x26, 0x20, 0x28, 0x28, 0x2a, 0x28, + 0x2c, 0x2c, 0x2e, + 0x20, 0x30, 0x30, 0x32, 0x30, 0x34, 0x34, 0x36, 0x30, 0x38, 0x38, 0x3a, + 0x38, 0x3c, 0x3c, 0x3e, + 0, 0x40, 0x40, 0x42, 0x40, 0x44, 0x44, 0x46, 0x40, 0x48, 0x48, 0x4a, 0x48, + 0x4c, 0x4c, 0x4e, + 0x40, 0x50, 0x50, 0x52, 0x50, 0x54, 0x54, 0x56, 0x50, 0x58, 0x58, 0x5a, + 0x58, 0x5c, 0x5c, 0x5e, + 0x40, 0x60, 0x60, 0x62, 0x60, 0x64, 0x64, 0x66, 0x60, 0x68, 0x68, 0x6a, + 0x68, 0x6c, 0x6c, 0x6e, + 0x60, 0x70, 0x70, 0x72, 0x70, 0x74, 0x74, 0x76, 0x70, 0x78, 0x78, 0x7a, + 0x78, 0x7c, 0x7c, 0x7e, + 0, 0x80, 0x80, 0x82, 0x80, 0x84, 0x84, 0x86, 0x80, 0x88, 0x88, 0x8a, 0x88, + 0x8c, 0x8c, 0x8e, + 0x80, 0x90, 0x90, 0x92, 0x90, 0x94, 0x94, 0x96, 0x90, 0x98, 0x98, 0x9a, + 0x98, 0x9c, 0x9c, 0x9e, + 0x80, 0xa0, 0xa0, 0xa2, 0xa0, 0xa4, 0xa4, 0xa6, 0xa0, 0xa8, 0xa8, 0xaa, + 0xa8, 0xac, 0xac, 0xae, + 0xa0, 0xb0, 0xb0, 0xb2, 0xb0, 0xb4, 0xb4, 0xb6, 0xb0, 0xb8, 0xb8, 0xba, + 0xb8, 0xbc, 0xbc, 0xbe, + 0x80, 0xc0, 0xc0, 0xc2, 0xc0, 0xc4, 0xc4, 0xc6, 0xc0, 0xc8, 0xc8, 0xca, + 0xc8, 0xcc, 0xcc, 0xce, + 0xc0, 0xd0, 0xd0, 0xd2, 0xd0, 0xd4, 0xd4, 0xd6, 0xd0, 0xd8, 0xd8, 0xda, + 0xd8, 0xdc, 0xdc, 0xde, + 0xc0, 0xe0, 0xe0, 0xe2, 0xe0, 0xe4, 0xe4, 0xe6, 0xe0, 0xe8, 0xe8, 0xea, + 0xe8, 0xec, 0xec, 0xee, + 0xe0, 0xf0, 0xf0, 0xf2, 0xf0, 0xf4, 0xf4, 0xf6, 0xf0, 0xf8, 0xf8, 0xfa, + 0xf8, 0xfc, 0xfc, 0xfe +}; +static int cp_maxes[128] = { + 100, + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, + 194, //! + 100, //" + 182, //# + 224, //$ + 203, //% + 242, //& + 245, //' + 226, //( + 190, //) + 244, //* + 195, //+ + 254, //, + 253, //- + 253, //. + 206, /// + 253, //0 + 234, //1 + 252, //2 + 246, //3 + 253, //4 + 160, //5 + 202, //6 + 199, //7 + 171, //8 + 227, //9 + 208, //: + 188, //; + 60, //< + 221, //= + 138, //> + 108, //? + 98, //@ + 251, //A + 214, //B + 230, //C + 252, //D + 237, //E + 217, //F + 233, //G + 174, //H + 216, //I + 210, //J + 252, //K + 253, //L + 233, //M + 243, //N + 240, //O + 230, //P + 167, //Q + 248, //R + 250, //S + 232, //T + 209, //U + 193, //V + 254, //W + 146, //X + 198, //Y + 107, //Z + 167, //[ + 163, // + 73, //] + 16, //^ + 199, //_ + 162, //` + 251, //a + 250, //b + 254, //c + 253, //d + 252, //e + 253, //f + 248, //g + 251, //h + 254, //i + 201, //j + 224, //k + 253, //l + 242, //m + 254, //n + 254, //o + 253, //p + 246, //q + 254, //r + 254, //s + 254, //t + 245, //u + 221, //v + 230, //w + 251, //x + 243, //y + 133, //z + 35, //{ + 100, //| + 143, //} + 100, //~ + 100 +}; + +static float cp_ratios[128] = { + 1.5f, + 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, + 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, + 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, + 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, + 2.24775, //! + 1.5, //" + 1.90376, //# + 1.61443, //$ + 1.87857, //% + 2.29167, //& + 7.4, //' + 4.7, //( + 9.4, //) + 2.13014, //* + 1.53175, //+ + 2.86957, //, + 7.4, //- + 7.4, //. + 9.4, /// + 8.1, //0 + 12.6, //1 + 2.7439, //2 + 4.22222, //3 + 2.57447, //4 + 2.93902, //5 + 4.23684, //6 + 6, //7 + 2.78889, //8 + 3.55, //9 + 8.5, //: + 2.4, //; + 1.5, //< + 1.94737, //= + 1.89394, //> + 1.5, //? + 1.5, //@ + 3.125, //A + 5.5, //B + 6.1, //C + 6, //D + 2.78205, //E + 2.03763, //F + 2.73256, //G + 2.57692, //H + 11.8, //I + 7.1, //J + 1.85227, //K + 7.4, //L + 2.26056, //M + 2.46078, //N + 6.85714, //O + 3.45238, //P + 2.47222, //Q + 3.74, //R + 10.2, //S + 3.08065, //T + 6.1, //U + 9.5, //V + 7.1, //W + 7.9, //X + 2.55714, //Y + 7.7, //Z + 2, //[ + 1.5, // + 2.55714, //] + 1.5, //^ + 1.80065, //_ + 1.69512, //` + 5.34, //a + 7.3, //b + 6.43333, //c + 4.10606, //d + 4.41667, //e + 12.6, //f + 3.7093, //g + 2.38889, //h + 5.5, //i + 4.03125, //j + 2.24561, //k + 11.5, //l + 3.5, //m + 5.63333, //n + 11, //o + 2.52667, //p + 2.1129, //q + 6.56667, //r + 6.42857, //s + 11.4, //t + 3.62, //u + 2.77273, //v + 2.90909, //w + 6.5, //x + 4.98387, //y + 2.92857, //z + 1.5, //{ + 1.5, //| + 2.02128, //} + 1.5, //~ + 1.5f +}; +static INT8 miss_table[256] = { + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 1, 1, 1, 0 +}; + +static UINT32 EvidenceTableMask; + +static UINT32 MultTruncShiftBits; + +static UINT32 TableTruncShiftBits; + +UINT32 EvidenceMultMask; + +static INT16 LocalMatcherMultiplier; + +make_int_var (ClassPrunerThreshold, 229, MakeClassPrunerThreshold, +16, 20, SetClassPrunerThreshold, +"Class Pruner Threshold 0-255: "); + +make_int_var (ClassPrunerMultiplier, 15, MakeClassPrunerMultiplier, +16, 21, SetClassPrunerMultiplier, +"Class Pruner Multiplier 0-255: "); + +make_int_var (IntegerMatcherMultiplier, 14, MakeIntegerMatcherMultiplier, +16, 22, SetIntegerMatcherMultiplier, +"Integer Matcher Multiplier 0-255: "); + +make_int_var (IntThetaFudge, 128, MakeIntThetaFudge, +16, 23, SetIntThetaFudge, +"Integer Matcher Theta Fudge 0-255: "); + +make_float_var (CPCutoffStrength, 0.15, MakeCPCutoffStrength, +16, 24, SetCPCutoffStrength, +"Class Pruner CutoffStrength: "); + +make_int_var (EvidenceTableBits, 9, MakeEvidenceTableBits, +16, 25, SetEvidenceTableBits, +"Bits in Similarity to Evidence Lookup 8-9: "); + +make_int_var (IntEvidenceTruncBits, 14, MakeIntEvidenceTruncBits, +16, 26, SetIntEvidenceTruncBits, +"Integer Evidence Truncation Bits (Distance) 8-14: "); + +make_float_var (SEExponentialMultiplier, 0, MakeSEExponentialMultiplier, +16, 27, SetSEExponentialMultiplier, +"Similarity to Evidence Table Exponential Multiplier: "); + +make_float_var (SimilarityCenter, 0.0075, MakeSimilarityCenter, +16, 28, SetSimilarityCenter, "Center of Similarity Curve: "); + +make_int_var (AdaptProtoThresh, 230, MakeAdaptProtoThresh, +16, 29, SetAdaptProtoThresh, +"Threshold for good protos during adaptive 0-255: "); + +make_int_var (AdaptFeatureThresh, 230, MakeAdaptFeatureThresh, +16, 30, SetAdaptFeatureThresh, +"Threshold for good features during adaptive 0-255: "); +//extern int display_ratings; +//extern "C" int newcp_ratings_on; +//extern "C" double newcp_prune_threshold; +//extern "C" double tessedit_cp_ratio; +//extern "C" int feature_prune_percentile; +//extern INT32 cp_maps[4]; + +int protoword_lookups; +int zero_protowords; +int proto_shifts; +int set_proto_bits; +int config_shifts; +int set_config_bits; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int ClassPruner(INT_TEMPLATES IntTemplates, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + CLASS_NORMALIZATION_ARRAY NormalizationFactors, + CLASS_CUTOFF_ARRAY ExpectedNumFeatures, + CLASS_PRUNER_RESULTS Results, + int Debug) { +/* + ** Parameters: + ** IntTemplates Class pruner tables + ** NumFeatures Number of features in blob + ** Features Array of features + ** NormalizationFactors Array of fudge factors from blob + ** normalization process + ** (by CLASS_INDEX) + ** ExpectedNumFeatures Array of expected number of features + ** for each class + ** (by CLASS_INDEX) + ** Results Sorted Array of pruned classes + ** (by CLASS_ID) + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** ClassPrunerThreshold Cutoff threshold + ** ClassPrunerMultiplier Normalization factor multiplier + ** Operation: + ** Prune the classes using a modified fast match table. + ** Return a sorted list of classes along with the number + ** of pruned classes in that list. + ** Return: Number of pruned classes. + ** Exceptions: none + ** History: Tue Feb 19 10:24:24 MST 1991, RWM, Created. + */ + UINT32 PrunerWord; + INT32 class_index; //index to class + int Word; + UINT32 *BasePrunerAddress; + UINT32 feature_address; //current feature index + INT_FEATURE feature; //current feature + CLASS_PRUNER *ClassPruner; + int PrunerSet; + int NumPruners; + INT32 feature_index; //current feature + + static INT32 ClassCount[MAX_NUM_CLASSES - 1]; + static INT16 NormCount[MAX_NUM_CLASSES - 1]; + static INT16 SortKey[MAX_NUM_CLASSES]; + static UINT8 SortIndex[MAX_NUM_CLASSES]; + CLASS_INDEX Class; + int out_class; + int MaxNumClasses; + int MaxCount; + int NumClasses; + FLOAT32 max_rating; //max allowed rating + INT32 *ClassCountPtr; + INT8 classch; + + MaxNumClasses = NumClassesIn (IntTemplates); + + /* Clear Class Counts */ + ClassCountPtr = &(ClassCount[0]); + for (Class = 0; Class < MaxNumClasses; Class++) { + *ClassCountPtr++ = 0; + } + + /* Update Class Counts */ + NumPruners = NumClassPrunersIn (IntTemplates); + for (feature_index = 0; feature_index < NumFeatures; feature_index++) { + feature = &Features[feature_index]; + feature->CP_misses = 0; + feature_address = (((feature->X * NUM_CP_BUCKETS >> 8) * NUM_CP_BUCKETS + + + (feature->Y * NUM_CP_BUCKETS >> 8)) * + NUM_CP_BUCKETS + + (feature->Theta * NUM_CP_BUCKETS >> 8)) << 1; + ClassPruner = ClassPrunersFor (IntTemplates); + class_index = 0; + for (PrunerSet = 0; PrunerSet < NumPruners; PrunerSet++, ClassPruner++) { + BasePrunerAddress = (UINT32 *) (*ClassPruner) + feature_address; + + for (Word = 0; Word < WERDS_PER_CP_VECTOR; Word++) { + PrunerWord = *BasePrunerAddress++; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + } + } + } + + /* Adjust Class Counts for Number of Expected Features */ + for (Class = 0; Class < MaxNumClasses; Class++) + if (NumFeatures < ExpectedNumFeatures[Class]) + ClassCount[Class] = + (int) (((FLOAT32) (ClassCount[Class] * NumFeatures)) / + (NumFeatures + + CPCutoffStrength * (ExpectedNumFeatures[Class] - + NumFeatures))); + + /* Adjust Class Counts for Normalization Factors */ + MaxCount = 0; + for (Class = 0; Class < MaxNumClasses; Class++) { + NormCount[Class] = ClassCount[Class] + - ((ClassPrunerMultiplier * NormalizationFactors[Class]) >> 8) + * cp_maps[3] / 3; + if (NormCount[Class] > MaxCount) + MaxCount = NormCount[Class]; + } + + /* Prune Classes */ + MaxCount *= ClassPrunerThreshold; + MaxCount >>= 8; + /* Select Classes */ + if (MaxCount < 1) + MaxCount = 1; + NumClasses = 0; + for (Class = 0; Class < MaxNumClasses; Class++) + if (NormCount[Class] >= MaxCount) { + NumClasses++; + SortIndex[NumClasses] = Class; + SortKey[NumClasses] = NormCount[Class]; + } + + /* Sort Classes using Heapsort Algorithm */ + if (NumClasses > 1) + HeapSort(NumClasses, SortKey, SortIndex); + + if (display_ratings > 1) { + cprintf ("CP:%d classes, %d features:\n", NumClasses, NumFeatures); + for (Class = 0; Class < NumClasses; Class++) { + classch = + ClassIdForIndex (IntTemplates, SortIndex[NumClasses - Class]); + cprintf ("%c:C=%d, E=%d, N=%d, Rat=%d\n", classch, + ClassCount[SortIndex[NumClasses - Class]], + ExpectedNumFeatures[SortIndex[NumClasses - Class]], + SortKey[NumClasses - Class], + (int) (10 + + 1000 * (1.0f - + SortKey[NumClasses - + Class] / ((float) cp_maps[3] * + NumFeatures)))); + } + if (display_ratings > 2) { + NumPruners = NumClassPrunersIn (IntTemplates); + for (feature_index = 0; feature_index < NumFeatures; + feature_index++) { + cprintf ("F=%3d,", feature_index); + feature = &Features[feature_index]; + feature->CP_misses = 0; + feature_address = + (((feature->X * NUM_CP_BUCKETS >> 8) * NUM_CP_BUCKETS + + (feature->Y * NUM_CP_BUCKETS >> 8)) * NUM_CP_BUCKETS + + (feature->Theta * NUM_CP_BUCKETS >> 8)) << 1; + ClassPruner = ClassPrunersFor (IntTemplates); + class_index = 0; + for (PrunerSet = 0; PrunerSet < NumPruners; + PrunerSet++, ClassPruner++) { + BasePrunerAddress = (UINT32 *) (*ClassPruner) + + feature_address; + + for (Word = 0; Word < WERDS_PER_CP_VECTOR; Word++) { + PrunerWord = *BasePrunerAddress++; + for (Class = 0; Class < 16; Class++, class_index++) { + if (NormCount[class_index] >= MaxCount) + cprintf (" %c=%d,", + ClassIdForIndex (IntTemplates, + class_index), + PrunerWord & 3); + PrunerWord >>= 2; + } + } + } + cprintf ("\n"); + } + cprintf ("Adjustments:"); + for (Class = 0; Class < MaxNumClasses; Class++) { + if (NormCount[Class] > MaxCount) + cprintf (" %c=%d,", + ClassIdForIndex (IntTemplates, Class), + -((ClassPrunerMultiplier * + NormalizationFactors[Class]) >> 8) * cp_maps[3] / + 3); + } + cprintf ("\n"); + } + } + + /* Set Up Results */ + max_rating = 0.0f; + for (Class = 0, out_class = 0; Class < NumClasses; Class++) { + Results[out_class].Class = + ClassIdForIndex (IntTemplates, SortIndex[NumClasses - Class]); + Results[out_class].config_mask = (UINT32) - 1; + Results[out_class].Rating = + 1.0 - SortKey[NumClasses - + Class] / ((float) cp_maps[3] * NumFeatures); + /**/ Results[out_class].Rating2 = + 1.0 - SortKey[NumClasses - + Class] / ((float) cp_maps[3] * NumFeatures); + if (tessedit_cp_ratio == 0.0 || Class == 0 + || Results[out_class].Rating * 1000 + 10 < + cp_maxes[Results[out_class].Class] + && Results[out_class].Rating * 1000 + 10 < + (Results[0].Rating * 1000 + + 10) * cp_ratios[Results[out_class].Class]) + out_class++; + } + NumClasses = out_class; + if (blob_type != 0) { + cp_classes = NumClasses; + if (NumClasses > 0) { + cp_chars[0] = Results[0].Class; + cp_ratings[0] = (int) (1000 * Results[0].Rating + 10); + cp_confs[0] = (int) (1000 * Results[0].Rating2 + 10); + if (NumClasses > 1) { + cp_chars[1] = Results[1].Class; + cp_ratings[1] = (int) (1000 * Results[1].Rating + 10); + cp_confs[1] = (int) (1000 * Results[1].Rating2 + 10); + } + else { + cp_chars[1] = '~'; + cp_ratings[1] = -1; + cp_confs[1] = -1; + } + } + else { + cp_chars[0] = '~'; + cp_ratings[0] = -1; + cp_confs[0] = -1; + } + cp_bestindex = -1; + cp_bestrating = -1; + cp_bestconf = -1; + for (Class = 0; Class < NumClasses; Class++) { + classch = Results[Class].Class; + if (classch == blob_answer) { + cp_bestindex = Class; + cp_bestrating = (int) (1000 * Results[Class].Rating + 10); + cp_bestconf = (int) (1000 * Results[Class].Rating2 + 10); + } + } + } + return NumClasses; + +} + + +/*---------------------------------------------------------------------------*/ +int feature_pruner(INT_TEMPLATES IntTemplates, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 NumClasses, + CLASS_PRUNER_RESULTS Results) { +/* + ** Parameters: + ** IntTemplates Class pruner tables + ** NumFeatures Number of features in blob + ** Features Array of features + ** NormalizationFactors Array of fudge factors from blob + ** normalization process + ** (by CLASS_INDEX) + ** ExpectedNumFeatures Array of expected number of features + ** for each class + ** (by CLASS_INDEX) + ** Results Sorted Array of pruned classes + ** (by CLASS_ID) + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** ClassPrunerThreshold Cutoff threshold + ** ClassPrunerMultiplier Normalization factor multiplier + ** Operation: + ** Prune the classes using a modified fast match table. + ** Return a sorted list of classes along with the number + ** of pruned classes in that list. + ** Return: Number of pruned classes. + ** Exceptions: none + ** History: Tue Feb 19 10:24:24 MST 1991, RWM, Created. + */ + UINT32 PrunerWord; + CLASS_PRUNER *ClassPruner; + INT32 class_index; //index to class + INT32 result_index; //CP results index + int PrunerSet; + int NumPruners; + int Word; + INT_FEATURE feature; //current feature + INT32 feature_index; //current feature + INT32 CP_misses; //missed features + UINT32 feature_address; //current feature index + UINT32 *BasePrunerAddress; + int MaxNumClasses; + UINT32 class_mask[CLASS_MASK_SIZE]; + INT32 miss_histogram[MAX_NUM_CLASSES]; + + MaxNumClasses = NumClassesIn (IntTemplates); + for (class_index = 0; class_index < MaxNumClasses; class_index++) + miss_histogram[class_index] = 0; + + /* Create class mask */ + for (class_index = 0; class_index < CLASS_MASK_SIZE; class_index++) + class_mask[class_index] = (UINT32) - 1; + for (result_index = 0; result_index < NumClasses; result_index++) { + class_index = + IndexForClassId (IntTemplates, Results[result_index].Class); + class_mask[class_index / CLASSES_PER_CP_WERD] &= + ~(3 << (class_index % CLASSES_PER_CP_WERD) * 2); + } + + /* Update Class Counts */ + NumPruners = NumClassPrunersIn (IntTemplates); + for (feature_index = 0; feature_index < NumFeatures; feature_index++) { + feature = &Features[feature_index]; + feature_address = (((feature->X * NUM_CP_BUCKETS >> 8) * NUM_CP_BUCKETS + + + (feature->Y * NUM_CP_BUCKETS >> 8)) * + NUM_CP_BUCKETS + + (feature->Theta * NUM_CP_BUCKETS >> 8)) << 1; + CP_misses = 0; + ClassPruner = ClassPrunersFor (IntTemplates); + class_index = 0; + for (PrunerSet = 0; PrunerSet < NumPruners; PrunerSet++, ClassPruner++) { + BasePrunerAddress = (UINT32 *) (*ClassPruner) + feature_address; + + for (Word = 0; Word < WERDS_PER_CP_VECTOR; Word++) { + PrunerWord = *BasePrunerAddress++; + PrunerWord |= class_mask[class_index++]; + CP_misses += miss_table[PrunerWord & 255]; + PrunerWord >>= 8; + CP_misses += miss_table[PrunerWord & 255]; + PrunerWord >>= 8; + CP_misses += miss_table[PrunerWord & 255]; + PrunerWord >>= 8; + CP_misses += miss_table[PrunerWord & 255]; + } + } + feature->CP_misses = CP_misses; + if (display_ratings > 1) { + cprintf ("F=%d: misses=%d\n", feature_index, CP_misses); + } + miss_histogram[CP_misses]++; + } + + CP_misses = 0; + feature_index = NumFeatures * feature_prune_percentile / 100; + for (class_index = MaxNumClasses - 1; class_index >= 0; class_index--) { + CP_misses += miss_histogram[class_index]; + if (CP_misses >= feature_index) + break; + } + + if (display_ratings > 1) { + cprintf ("FP:Selected miss factor of %d for %d features (%g%%)\n", + class_index, CP_misses, 100.0 * CP_misses / NumFeatures); + } + return class_index; +} + + +/*---------------------------------------------------------------------------*/ +int prune_configs(INT_TEMPLATES IntTemplates, + INT32 min_misses, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + CLASS_NORMALIZATION_ARRAY NormalizationFactors, + INT32 class_count, + UINT16 BlobLength, + CLASS_PRUNER_RESULTS Results, + int Debug) { +/* + ** Parameters: + ** IntTemplates Class pruner tables + ** NumFeatures Number of features in blob + ** Features Array of features + ** NormalizationFactors Array of fudge factors from blob + ** normalization process + ** (by CLASS_INDEX) + ** ExpectedNumFeatures Array of expected number of features + ** for each class + ** (by CLASS_INDEX) + ** Results Sorted Array of pruned classes + ** (by CLASS_ID) + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** ClassPrunerThreshold Cutoff threshold + ** ClassPrunerMultiplier Normalization factor multiplier + ** Operation: + ** Prune the classes using a modified fast match table. + ** Return a sorted list of classes along with the number + ** of pruned classes in that list. + ** Return: Number of pruned classes. + ** Exceptions: none + ** History: Tue Feb 19 10:24:24 MST 1991, RWM, Created. + */ + INT32 classindex; //current Results index + CLASS_INDEX Class; //current class + INT_CLASS ClassTemplate; //info on current class + FLOAT32 best_rating; //best over all classes + FLOAT32 best_class_rating; //best over all classes + INT32 output_count; //number of classes out + INT32 best_index; //for sorting + INT_RESULT_STRUCT IntResult; + //results of pruning + CLASS_PRUNER_RESULTS new_results; + + best_class_rating = 9999.0f; + for (classindex = 0; classindex < class_count; classindex++) { + Class = IndexForClassId (IntTemplates, Results[classindex].Class); + ClassTemplate = ClassForIndex (IntTemplates, Class); + PruningMatcher (ClassTemplate, BlobLength, NumFeatures, Features, + min_misses, NormalizationFactors[Class], + &IntResult, Debug); + + /* Prune configs */ + //save old rating + new_results[classindex].Rating2 = Results[classindex].Rating; + //save new rating + new_results[classindex].Rating = IntResult.Rating; + //save new rating + new_results[classindex].config_mask = (1 << IntResult.Config) | (1 << IntResult.Config2); + //save old class + new_results[classindex].Class = Results[classindex].Class; + + if (display_ratings > 1) { + cprintf ("PC:%c: old=%g, best_rating=%g, config1=%d, config2=%d\n", + Results[classindex].Class, + Results[classindex].Rating2, + IntResult.Rating, IntResult.Config, IntResult.Config2); + } + + if (IntResult.Rating < best_class_rating) + best_class_rating = IntResult.Rating; + } + /* Select Classes */ + best_class_rating *= newcp_prune_threshold; + + output_count = 0; + do { + best_rating = best_class_rating; + best_index = -1; + for (classindex = 0; classindex < class_count; classindex++) { + if (new_results[classindex].Rating <= best_rating) { + best_rating = new_results[classindex].Rating; + best_index = classindex; + } + } + if (best_index >= 0) { + Results[output_count].Class = new_results[best_index].Class; + Results[output_count].Rating = best_rating; + Results[output_count].Rating2 = new_results[best_index].Rating2; + Results[output_count].config_mask = + new_results[best_index].config_mask; + new_results[best_index].Rating = 9999.0f; + output_count++; + } + } + while (best_index >= 0); + + if (display_ratings > 1) { + cprintf ("%d classes reduced to %d\n", class_count, output_count); + for (classindex = 0; classindex < output_count; classindex++) { + cprintf ("%c=%g/%g/0x%x, ", + Results[classindex].Class, + Results[classindex].Rating, + Results[classindex].Rating2, + Results[classindex].config_mask); + } + cprintf ("\n"); + } + return output_count; +} + + +/*---------------------------------------------------------------------------*/ +void PruningMatcher(INT_CLASS ClassTemplate, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 min_misses, + UINT8 NormalizationFactor, + INT_RESULT Result, + int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** BlobLength Length of unormalized blob + ** NumFeatures Number of features in blob + ** Features Array of features + ** NormalizationFactor Fudge factor from blob + ** normalization process + ** Result Class rating & configuration: + ** (0.0 -> 1.0), 0=good, 1=bad + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** LocalMatcherMultiplier Normalization factor multiplier + ** IntThetaFudge Theta fudge factor used for + ** evidence calculation + ** Operation: + ** IntegerMatcher returns the best configuration and rating + ** for a single class. The class matched against is determined + ** by the uniqueness of the ClassTemplate parameter. The + ** best rating and its associated configuration are returned. + ** Return: + ** Exceptions: none + ** History: Tue Feb 19 16:36:23 MST 1991, RWM, Created. + */ + static UINT8 FeatureEvidence[MAX_NUM_CONFIGS]; + static int SumOfFeatureEvidence[MAX_NUM_CONFIGS]; + int *IntPointer; + int Feature; + int BestMatch; + int used_features; + int NumConfigs; + + if (MatchDebuggingOn (Debug)) + cprintf ("Pruning Matcher -------------------------------------------\n"); + + IntPointer = SumOfFeatureEvidence; + for (NumConfigs = NumIntConfigsIn (ClassTemplate); NumConfigs > 0; + NumConfigs--) + *IntPointer++ = 0; + + for (Feature = 0, used_features = 0; Feature < NumFeatures; Feature++) { + if (Features[Feature].CP_misses >= min_misses) { + PMUpdateTablesForFeature (ClassTemplate, Feature, + &(Features[Feature]), FeatureEvidence, + SumOfFeatureEvidence, Debug); + used_features++; + } + } + + IMNormalizeSumOfEvidences(ClassTemplate, + SumOfFeatureEvidence, + NumFeatures, + used_features); + + BestMatch = + IMFindBestMatch(ClassTemplate, + SumOfFeatureEvidence, + BlobLength, + NormalizationFactor, + Result); + +#ifndef GRAPHICS_DISABLED + if (PrintMatchSummaryOn (Debug)) + IMDebugBestMatch(BestMatch, Result, BlobLength, NormalizationFactor); +#endif + + if (MatchDebuggingOn (Debug)) + cprintf ("Match Complete --------------------------------------------\n"); + +} + + +/*---------------------------------------------------------------------------*/ +void config_mask_to_proto_mask(INT_CLASS ClassTemplate, + BIT_VECTOR config_mask, + BIT_VECTOR proto_mask) { + UINT32 ConfigWord; + int ProtoSetIndex; + UINT32 ProtoNum; + PROTO_SET ProtoSet; + int NumProtos; + UINT32 ActualProtoNum; + + NumProtos = NumIntProtosIn (ClassTemplate); + + zero_all_bits (proto_mask, WordsInVectorOfSize (MAX_NUM_PROTOS)); + for (ProtoSetIndex = 0; ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); + for (ProtoNum = 0; + ((ProtoNum < PROTOS_PER_PROTO_SET) + && (ActualProtoNum < NumProtos)); ProtoNum++, ActualProtoNum++) { + ConfigWord = (ProtoSet->Protos[ProtoNum]).Configs[0]; + ConfigWord &= *config_mask; + if (ConfigWord != 0) { + proto_mask[ActualProtoNum / 32] |= 1 << (ActualProtoNum % 32); + } + } + } +} + + +/*---------------------------------------------------------------------------*/ +void IntegerMatcher(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 min_misses, + UINT8 NormalizationFactor, + INT_RESULT Result, + int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** BlobLength Length of unormalized blob + ** NumFeatures Number of features in blob + ** Features Array of features + ** NormalizationFactor Fudge factor from blob + ** normalization process + ** Result Class rating & configuration: + ** (0.0 -> 1.0), 0=good, 1=bad + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** LocalMatcherMultiplier Normalization factor multiplier + ** IntThetaFudge Theta fudge factor used for + ** evidence calculation + ** Operation: + ** IntegerMatcher returns the best configuration and rating + ** for a single class. The class matched against is determined + ** by the uniqueness of the ClassTemplate parameter. The + ** best rating and its associated configuration are returned. + ** Return: + ** Exceptions: none + ** History: Tue Feb 19 16:36:23 MST 1991, RWM, Created. + */ + static UINT8 FeatureEvidence[MAX_NUM_CONFIGS]; + static int SumOfFeatureEvidence[MAX_NUM_CONFIGS]; + static UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]; + int Feature; + int BestMatch; + int used_features; + + if (MatchDebuggingOn (Debug)) + cprintf ("Integer Matcher -------------------------------------------\n"); + + IMClearTables(ClassTemplate, SumOfFeatureEvidence, ProtoEvidence); + + for (Feature = 0, used_features = 0; Feature < NumFeatures; Feature++) { + if (Features[Feature].CP_misses >= min_misses) { + IMUpdateTablesForFeature (ClassTemplate, ProtoMask, ConfigMask, + Feature, &(Features[Feature]), + FeatureEvidence, SumOfFeatureEvidence, + ProtoEvidence, Debug); + used_features++; + } + } + +#ifndef GRAPHICS_DISABLED + if (PrintProtoMatchesOn (Debug) || PrintMatchSummaryOn (Debug)) + IMDebugFeatureProtoError(ClassTemplate, + ProtoMask, + ConfigMask, + SumOfFeatureEvidence, + ProtoEvidence, + NumFeatures, + Debug); + + if (DisplayProtoMatchesOn (Debug)) + IMDisplayProtoDebugInfo(ClassTemplate, + ProtoMask, + ConfigMask, + ProtoEvidence, + Debug); + + if (DisplayFeatureMatchesOn (Debug)) + IMDisplayFeatureDebugInfo(ClassTemplate, + ProtoMask, + ConfigMask, + NumFeatures, + Features, + Debug); +#endif + + IMUpdateSumOfProtoEvidences(ClassTemplate, + ConfigMask, + SumOfFeatureEvidence, + ProtoEvidence, + NumFeatures); + + IMNormalizeSumOfEvidences(ClassTemplate, + SumOfFeatureEvidence, + NumFeatures, + used_features); + + BestMatch = + IMFindBestMatch(ClassTemplate, + SumOfFeatureEvidence, + BlobLength, + NormalizationFactor, + Result); + +#ifndef GRAPHICS_DISABLED + if (PrintMatchSummaryOn (Debug)) + IMDebugBestMatch(BestMatch, Result, BlobLength, NormalizationFactor); + + if (MatchDebuggingOn (Debug)) + cprintf ("Match Complete --------------------------------------------\n"); +#endif + +} + + +/*---------------------------------------------------------------------------*/ +int FindGoodProtos(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + PROTO_ID *ProtoArray, + int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** ProtoMask AND Mask for proto word + ** ConfigMask AND Mask for config word + ** BlobLength Length of unormalized blob + ** NumFeatures Number of features in blob + ** Features Array of features + ** ProtoArray Array of good protos + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** LocalMatcherMultiplier Normalization factor multiplier + ** IntThetaFudge Theta fudge factor used for + ** evidence calculation + ** AdaptProtoThresh Threshold for good protos + ** Operation: + ** FindGoodProtos finds all protos whose normalized proto-evidence + ** exceed AdaptProtoThresh. The list is ordered by increasing + ** proto id number. + ** Return: + ** Number of good protos in ProtoArray. + ** Exceptions: none + ** History: Tue Mar 12 17:09:26 MST 1991, RWM, Created + */ + static UINT8 FeatureEvidence[MAX_NUM_CONFIGS]; + static int SumOfFeatureEvidence[MAX_NUM_CONFIGS]; + static UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]; + int Feature; + register UINT8 *UINT8Pointer; + register int ProtoIndex; + int NumProtos; + int NumGoodProtos; + UINT16 ActualProtoNum; + register int Temp; + + /* DEBUG opening heading */ + if (MatchDebuggingOn (Debug)) + cprintf + ("Find Good Protos -------------------------------------------\n"); + + IMClearTables(ClassTemplate, SumOfFeatureEvidence, ProtoEvidence); + + for (Feature = 0; Feature < NumFeatures; Feature++) + IMUpdateTablesForFeature (ClassTemplate, ProtoMask, ConfigMask, Feature, + &(Features[Feature]), FeatureEvidence, + SumOfFeatureEvidence, ProtoEvidence, Debug); + +#ifndef GRAPHICS_DISABLED + if (PrintProtoMatchesOn (Debug) || PrintMatchSummaryOn (Debug)) + IMDebugFeatureProtoError(ClassTemplate, + ProtoMask, + ConfigMask, + SumOfFeatureEvidence, + ProtoEvidence, + NumFeatures, + Debug); +#endif + + /* Average Proto Evidences & Find Good Protos */ + NumProtos = NumIntProtosIn (ClassTemplate); + NumGoodProtos = 0; + for (ActualProtoNum = 0; ActualProtoNum < NumProtos; ActualProtoNum++) { + /* Compute Average for Actual Proto */ + Temp = 0; + UINT8Pointer = &(ProtoEvidence[ActualProtoNum][0]); + for (ProtoIndex = LengthForProtoId (ClassTemplate, ActualProtoNum); + ProtoIndex > 0; ProtoIndex--, UINT8Pointer++) + Temp += *UINT8Pointer; + + Temp /= LengthForProtoId (ClassTemplate, ActualProtoNum); + + /* Find Good Protos */ + if (Temp >= AdaptProtoThresh) { + *ProtoArray = ActualProtoNum; + ProtoArray++; + NumGoodProtos++; + } + } + + if (MatchDebuggingOn (Debug)) + cprintf ("Match Complete --------------------------------------------\n"); + + return NumGoodProtos; + +} + + +/*---------------------------------------------------------------------------*/ +int FindBadFeatures(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + FEATURE_ID *FeatureArray, + int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** ProtoMask AND Mask for proto word + ** ConfigMask AND Mask for config word + ** BlobLength Length of unormalized blob + ** NumFeatures Number of features in blob + ** Features Array of features + ** FeatureArray Array of bad features + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** LocalMatcherMultiplier Normalization factor multiplier + ** IntThetaFudge Theta fudge factor used for + ** evidence calculation + ** AdaptFeatureThresh Threshold for bad features + ** Operation: + ** FindBadFeatures finds all features whose maximum feature-evidence + ** was less than AdaptFeatureThresh. The list is ordered by increasing + ** feature number. + ** Return: + ** Number of bad features in FeatureArray. + ** Exceptions: none + ** History: Tue Mar 12 17:09:26 MST 1991, RWM, Created + */ + static UINT8 FeatureEvidence[MAX_NUM_CONFIGS]; + static int SumOfFeatureEvidence[MAX_NUM_CONFIGS]; + static UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]; + int Feature; + register UINT8 *UINT8Pointer; + register int ConfigNum; + int NumConfigs; + int NumBadFeatures; + register int Temp; + + /* DEBUG opening heading */ + if (MatchDebuggingOn (Debug)) + cprintf + ("Find Bad Features -------------------------------------------\n"); + + IMClearTables(ClassTemplate, SumOfFeatureEvidence, ProtoEvidence); + + NumBadFeatures = 0; + NumConfigs = NumIntConfigsIn (ClassTemplate); + for (Feature = 0; Feature < NumFeatures; Feature++) { + IMUpdateTablesForFeature (ClassTemplate, ProtoMask, ConfigMask, Feature, + &(Features[Feature]), FeatureEvidence, + SumOfFeatureEvidence, ProtoEvidence, Debug); + + /* Find Best Evidence for Current Feature */ + Temp = 0; + UINT8Pointer = FeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, UINT8Pointer++) + if (*UINT8Pointer > Temp) + Temp = *UINT8Pointer; + + /* Find Bad Features */ + if (Temp < AdaptFeatureThresh) { + *FeatureArray = Feature; + FeatureArray++; + NumBadFeatures++; + } + } + +#ifndef GRAPHICS_DISABLED + if (PrintProtoMatchesOn (Debug) || PrintMatchSummaryOn (Debug)) + IMDebugFeatureProtoError(ClassTemplate, + ProtoMask, + ConfigMask, + SumOfFeatureEvidence, + ProtoEvidence, + NumFeatures, + Debug); +#endif + + if (MatchDebuggingOn (Debug)) + cprintf ("Match Complete --------------------------------------------\n"); + + return NumBadFeatures; + +} + + +/*---------------------------------------------------------------------------*/ +void InitIntegerMatcher() { + int i; + UINT32 IntSimilarity; + double Similarity; + double Evidence; + double ScaleFactor; + + /* Set default mode of operation of IntegerMatcher */ + SetCharNormMatch(); + + /* Initialize table for evidence to similarity lookup */ + for (i = 0; i < SE_TABLE_SIZE; i++) { + IntSimilarity = i << (27 - SE_TABLE_BITS); + Similarity = ((double) IntSimilarity) / 65536.0 / 65536.0; + Evidence = Similarity / SimilarityCenter; + Evidence *= Evidence; + Evidence += 1.0; + Evidence = 1.0 / Evidence; + Evidence *= 255.0; + + if (SEExponentialMultiplier > 0.0) { + ScaleFactor = 1.0 - exp (-SEExponentialMultiplier) * + exp (SEExponentialMultiplier * ((double) i / SE_TABLE_SIZE)); + if (ScaleFactor > 1.0) + ScaleFactor = 1.0; + if (ScaleFactor < 0.0) + ScaleFactor = 0.0; + Evidence *= ScaleFactor; + } + + SimilarityEvidenceTable[i] = (UINT8) (Evidence + 0.5); + } + + /* Initialize evidence computation variables */ + EvidenceTableMask = + ((1 << EvidenceTableBits) - 1) << (9 - EvidenceTableBits); + MultTruncShiftBits = (14 - IntEvidenceTruncBits); + TableTruncShiftBits = (27 - SE_TABLE_BITS - (MultTruncShiftBits << 1)); + EvidenceMultMask = ((1 << IntEvidenceTruncBits) - 1); + +} + + +/*---------------------------------------------------------------------------*/ +void InitIntegerMatcherVars() { + MakeClassPrunerThreshold(); + MakeClassPrunerMultiplier(); + MakeIntegerMatcherMultiplier(); + MakeIntThetaFudge(); + MakeCPCutoffStrength(); + MakeEvidenceTableBits(); + MakeIntEvidenceTruncBits(); + MakeSEExponentialMultiplier(); + MakeSimilarityCenter(); +} + + +/*-------------------------------------------------------------------------*/ +void PrintIntMatcherStats(FILE *f) { + fprintf (f, "protoword_lookups=%d, zero_protowords=%d, proto_shifts=%d\n", + protoword_lookups, zero_protowords, proto_shifts); + fprintf (f, "set_proto_bits=%d, config_shifts=%d, set_config_bits=%d\n", + set_proto_bits, config_shifts, set_config_bits); +} + + +/*-------------------------------------------------------------------------*/ +void SetProtoThresh(FLOAT32 Threshold) { + AdaptProtoThresh = (int) (255 * Threshold); + if (AdaptProtoThresh < 0) + AdaptProtoThresh = 0; + if (AdaptProtoThresh > 255) + AdaptProtoThresh = 255; +} + + +/*---------------------------------------------------------------------------*/ +void SetFeatureThresh(FLOAT32 Threshold) { + AdaptFeatureThresh = (int) (255 * Threshold); + if (AdaptFeatureThresh < 0) + AdaptFeatureThresh = 0; + if (AdaptFeatureThresh > 255) + AdaptFeatureThresh = 255; +} + + +/*--------------------------------------------------------------------------*/ +void SetBaseLineMatch() { + LocalMatcherMultiplier = 0; +} + + +/*--------------------------------------------------------------------------*/ +void SetCharNormMatch() { + LocalMatcherMultiplier = IntegerMatcherMultiplier; +} + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void +IMClearTables (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]) { +/* + ** Parameters: + ** SumOfFeatureEvidence Sum of Feature Evidence Table + ** NumConfigs Number of Configurations + ** ProtoEvidence Prototype Evidence Table + ** NumProtos Number of Prototypes + ** Globals: + ** Operation: + ** Clear SumOfFeatureEvidence and ProtoEvidence tables. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register UINT8 *UINT8Pointer; + register int *IntPointer; + register int ConfigNum; + int NumConfigs; + register UINT16 ProtoNum; + int NumProtos; + register int ProtoIndex; + + NumProtos = NumIntProtosIn (ClassTemplate); + NumConfigs = NumIntConfigsIn (ClassTemplate); + + IntPointer = SumOfFeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, IntPointer++) + *IntPointer = 0; + UINT8Pointer = (UINT8 *) ProtoEvidence; + for (ProtoNum = 0; ProtoNum < NumProtos; ProtoNum++) + for (ProtoIndex = 0; ProtoIndex < MAX_PROTO_INDEX; + ProtoIndex++, UINT8Pointer++) + *UINT8Pointer = 0; + +} + + +/*---------------------------------------------------------------------------*/ +void +IMClearFeatureEvidenceTable (UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int NumConfigs) { +/* + ** Parameters: + ** FeatureEvidence Feature Evidence Table + ** NumConfigs Number of Configurations + ** Globals: + ** Operation: + ** Clear FeatureEvidence table. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register UINT8 *UINT8Pointer; + register int ConfigNum; + + UINT8Pointer = FeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, UINT8Pointer++) + *UINT8Pointer = 0; + +} + + +/*---------------------------------------------------------------------------*/ +void IMDebugConfiguration(int FeatureNum, + UINT16 ActualProtoNum, + UINT8 Evidence, + BIT_VECTOR ConfigMask, + UINT32 ConfigWord) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Print debugging information for Configuations + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + cprintf ("F = %3d, P = %3d, E = %3d, Configs = ", + FeatureNum, (int) ActualProtoNum, (int) Evidence); + while (ConfigWord) { + if (ConfigWord & 1) + cprintf ("1"); + else + cprintf ("0"); + ConfigWord >>= 1; + } + cprintf ("\n"); +} + + +/*---------------------------------------------------------------------------*/ +void IMDebugConfigurationSum(int FeatureNum, + UINT8 *FeatureEvidence, + INT32 ConfigCount) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Print debugging information for Configuations + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + int ConfigNum; + + cprintf ("F=%3d, C=", (int) FeatureNum); + + for (ConfigNum = 0; ConfigNum < ConfigCount; ConfigNum++) { + cprintf ("%4d", FeatureEvidence[ConfigNum]); + } + cprintf ("\n"); + +} + + +/*---------------------------------------------------------------------------*/ +void +PMUpdateTablesForFeature (INT_CLASS ClassTemplate, +int FeatureNum, +INT_FEATURE Feature, +UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** FeatureNum Current feature number (for DEBUG only) + ** Feature Pointer to a feature struct + ** FeatureEvidence Feature Evidence Table + ** SumOfFeatureEvidence Sum of Feature Evidence Table + ** ProtoEvidence Prototype Evidence Table + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** Operation: + ** For the given feature: prune protos, compute evidence, update Feature Evidence, + ** Proto Evidence, and Sum of Feature Evidence tables. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + UINT8 config_byte; + UINT8 proto_byte; + UINT8 Evidence; + INT32 config_offset; + UINT8 *UINT8Pointer; + UINT32 ConfigWord; + UINT32 ProtoWord; + INT32 M3; + INT32 A3; + UINT32 A4; + INT32 proto_word_offset; + INT32 proto_offset; + UINT32 ProtoNum; + UINT32 ActualProtoNum; + PROTO_SET ProtoSet; + UINT32 *ProtoPrunerPtr; + INT_PROTO Proto; + int ProtoSetIndex; + UINT32 XFeatureAddress; + UINT32 YFeatureAddress; + UINT32 ThetaFeatureAddress; + int *IntPointer; + int ConfigNum; + + IMClearFeatureEvidenceTable (FeatureEvidence, + NumIntConfigsIn (ClassTemplate)); + + /* Precompute Feature Address offset for Proto Pruning */ + XFeatureAddress = ((Feature->X >> 2) << 1); + YFeatureAddress = (NUM_PP_BUCKETS << 1) + ((Feature->Y >> 2) << 1); + ThetaFeatureAddress = (NUM_PP_BUCKETS << 2) + ((Feature->Theta >> 2) << 1); + + for (ProtoSetIndex = 0, ActualProtoNum = 0; + ProtoSetIndex < NumProtoSetsIn (ClassTemplate); ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ProtoPrunerPtr = (UINT32 *) ((*ProtoSet).ProtoPruner); + for (ProtoNum = 0; ProtoNum < PROTOS_PER_PROTO_SET; + ProtoNum += (PROTOS_PER_PROTO_SET >> 1), ActualProtoNum += + (PROTOS_PER_PROTO_SET >> 1), ProtoPrunerPtr++) { + /* Prune Protos of current Proto Set */ + ProtoWord = *(ProtoPrunerPtr + XFeatureAddress); + ProtoWord &= *(ProtoPrunerPtr + YFeatureAddress); + ProtoWord &= *(ProtoPrunerPtr + ThetaFeatureAddress); + + if (ProtoWord != 0) { + proto_byte = ProtoWord & 0xff; + ProtoWord >>= 8; + proto_word_offset = 0; + while (ProtoWord != 0 || proto_byte != 0) { + while (proto_byte == 0) { + proto_byte = ProtoWord & 0xff; + ProtoWord >>= 8; + proto_word_offset += 8; + } + proto_offset = offset_table[proto_byte] + proto_word_offset; + proto_byte = next_table[proto_byte]; + /* Compute Evidence */ + Proto = &(ProtoSet->Protos[ProtoNum + proto_offset]); + ConfigWord = Proto->Configs[0]; + A3 = (((Proto->A * (Feature->X - 128)) << 1) + - (Proto->B * (Feature->Y - 128)) + (Proto->C << 9)); + M3 = + (((INT8) (Feature->Theta - Proto->Angle)) * + IntThetaFudge) << 1; + + if (A3 < 0) + A3 = ~A3; + if (M3 < 0) + M3 = ~M3; + A3 >>= MultTruncShiftBits; + M3 >>= MultTruncShiftBits; + if (A3 > EvidenceMultMask) + A3 = EvidenceMultMask; + if (M3 > EvidenceMultMask) + M3 = EvidenceMultMask; + + A4 = (A3 * A3) + (M3 * M3); + A4 >>= TableTruncShiftBits; + if (A4 > EvidenceTableMask) + Evidence = 0; + else + Evidence = SimilarityEvidenceTable[A4]; + + UINT8Pointer = FeatureEvidence - 8; + config_byte = 0; + while (ConfigWord != 0 || config_byte != 0) { + while (config_byte == 0) { + config_byte = ConfigWord & 0xff; + ConfigWord >>= 8; + UINT8Pointer += 8; + } + config_offset = offset_table[config_byte]; + config_byte = next_table[config_byte]; + if (Evidence > UINT8Pointer[config_offset]) + UINT8Pointer[config_offset] = Evidence; + } + } + } + } + } + + if (PrintFeatureMatchesOn (Debug)) + IMDebugConfigurationSum (FeatureNum, FeatureEvidence, + NumIntConfigsIn (ClassTemplate)); + IntPointer = SumOfFeatureEvidence; + UINT8Pointer = FeatureEvidence; + for (ConfigNum = NumIntConfigsIn (ClassTemplate); ConfigNum > 0; + ConfigNum--) + *IntPointer++ += (*UINT8Pointer++); +} + + +/*---------------------------------------------------------------------------*/ +void +IMUpdateTablesForFeature (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +int FeatureNum, +INT_FEATURE Feature, +UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** FeatureNum Current feature number (for DEBUG only) + ** Feature Pointer to a feature struct + ** FeatureEvidence Feature Evidence Table + ** SumOfFeatureEvidence Sum of Feature Evidence Table + ** ProtoEvidence Prototype Evidence Table + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** Operation: + ** For the given feature: prune protos, compute evidence, update Feature Evidence, + ** Proto Evidence, and Sum of Feature Evidence tables. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register UINT32 ConfigWord; + register UINT32 ProtoWord; + register UINT32 ProtoNum; + register UINT32 ActualProtoNum; + UINT8 proto_byte; + INT32 proto_word_offset; + INT32 proto_offset; + UINT8 config_byte; + INT32 config_offset; + PROTO_SET ProtoSet; + UINT32 *ProtoPrunerPtr; + INT_PROTO Proto; + int ProtoSetIndex; + UINT8 Evidence; + UINT32 XFeatureAddress; + UINT32 YFeatureAddress; + UINT32 ThetaFeatureAddress; + register UINT8 *UINT8Pointer; + register int ProtoIndex; + UINT8 Temp; + register int *IntPointer; + int ConfigNum; + register INT32 M3; + register INT32 A3; + register UINT32 A4; + + IMClearFeatureEvidenceTable (FeatureEvidence, + NumIntConfigsIn (ClassTemplate)); + + /* Precompute Feature Address offset for Proto Pruning */ + XFeatureAddress = ((Feature->X >> 2) << 1); + YFeatureAddress = (NUM_PP_BUCKETS << 1) + ((Feature->Y >> 2) << 1); + ThetaFeatureAddress = (NUM_PP_BUCKETS << 2) + ((Feature->Theta >> 2) << 1); + + for (ProtoSetIndex = 0, ActualProtoNum = 0; + ProtoSetIndex < NumProtoSetsIn (ClassTemplate); ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ProtoPrunerPtr = (UINT32 *) ((*ProtoSet).ProtoPruner); + for (ProtoNum = 0; ProtoNum < PROTOS_PER_PROTO_SET; + ProtoNum += (PROTOS_PER_PROTO_SET >> 1), ActualProtoNum += + (PROTOS_PER_PROTO_SET >> 1), ProtoMask++, ProtoPrunerPtr++) { + /* Prune Protos of current Proto Set */ + ProtoWord = *(ProtoPrunerPtr + XFeatureAddress); + ProtoWord &= *(ProtoPrunerPtr + YFeatureAddress); + ProtoWord &= *(ProtoPrunerPtr + ThetaFeatureAddress); + ProtoWord &= *ProtoMask; + + if (ProtoWord != 0) { + proto_byte = ProtoWord & 0xff; + ProtoWord >>= 8; + proto_word_offset = 0; + while (ProtoWord != 0 || proto_byte != 0) { + while (proto_byte == 0) { + proto_byte = ProtoWord & 0xff; + ProtoWord >>= 8; + proto_word_offset += 8; + } + proto_offset = offset_table[proto_byte] + proto_word_offset; + proto_byte = next_table[proto_byte]; + Proto = &(ProtoSet->Protos[ProtoNum + proto_offset]); + ConfigWord = Proto->Configs[0]; + A3 = (((Proto->A * (Feature->X - 128)) << 1) + - (Proto->B * (Feature->Y - 128)) + (Proto->C << 9)); + M3 = + (((INT8) (Feature->Theta - Proto->Angle)) * + IntThetaFudge) << 1; + + if (A3 < 0) + A3 = ~A3; + if (M3 < 0) + M3 = ~M3; + A3 >>= MultTruncShiftBits; + M3 >>= MultTruncShiftBits; + if (A3 > EvidenceMultMask) + A3 = EvidenceMultMask; + if (M3 > EvidenceMultMask) + M3 = EvidenceMultMask; + + A4 = (A3 * A3) + (M3 * M3); + A4 >>= TableTruncShiftBits; + if (A4 > EvidenceTableMask) + Evidence = 0; + else + Evidence = SimilarityEvidenceTable[A4]; + + if (PrintFeatureMatchesOn (Debug)) + IMDebugConfiguration (FeatureNum, + ActualProtoNum + proto_offset, + Evidence, ConfigMask, ConfigWord); + + ConfigWord &= *ConfigMask; + + UINT8Pointer = FeatureEvidence - 8; + config_byte = 0; + while (ConfigWord != 0 || config_byte != 0) { + while (config_byte == 0) { + config_byte = ConfigWord & 0xff; + ConfigWord >>= 8; + UINT8Pointer += 8; + // config_shifts++; + } + config_offset = offset_table[config_byte]; + config_byte = next_table[config_byte]; + if (Evidence > UINT8Pointer[config_offset]) + UINT8Pointer[config_offset] = Evidence; + } + + UINT8Pointer = + &(ProtoEvidence[ActualProtoNum + proto_offset][0]); + for (ProtoIndex = + LengthForProtoId (ClassTemplate, + ActualProtoNum + proto_offset); + ProtoIndex > 0; ProtoIndex--, UINT8Pointer++) { + if (Evidence > *UINT8Pointer) { + Temp = *UINT8Pointer; + *UINT8Pointer = Evidence; + Evidence = Temp; + } + else if (Evidence == 0) + break; + } + } + } + } + } + + if (PrintFeatureMatchesOn (Debug)) + IMDebugConfigurationSum (FeatureNum, FeatureEvidence, + NumIntConfigsIn (ClassTemplate)); + IntPointer = SumOfFeatureEvidence; + UINT8Pointer = FeatureEvidence; + for (ConfigNum = NumIntConfigsIn (ClassTemplate); ConfigNum > 0; + ConfigNum--) + *IntPointer++ += (*UINT8Pointer++); + +} + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void +IMDebugFeatureProtoError (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +INT16 NumFeatures, int Debug) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Print debugging information for Configuations + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + UINT8 *UINT8Pointer; + int *IntPointer; + FLOAT32 ProtoConfigs[MAX_NUM_CONFIGS]; + int ConfigNum; + UINT32 ConfigWord; + int ProtoSetIndex; + UINT16 ProtoNum; + UINT8 ProtoWordNum; + PROTO_SET ProtoSet; + int ProtoIndex; + int NumProtos; + UINT16 ActualProtoNum; + int Temp; + int NumConfigs; + + NumProtos = NumIntProtosIn (ClassTemplate); + NumConfigs = NumIntConfigsIn (ClassTemplate); + + if (PrintMatchSummaryOn (Debug)) { + cprintf ("Configuration Mask:\n"); + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + cprintf ("%1d", (((*ConfigMask) >> ConfigNum) & 1)); + cprintf ("\n"); + + cprintf ("Feature Error for Configurations:\n"); + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + cprintf (" %5.1f", + 100.0 * (1.0 - + (FLOAT32) SumOfFeatureEvidence[ConfigNum] / + NumFeatures / 256.0)); + cprintf ("\n\n\n"); + } + + if (PrintMatchSummaryOn (Debug)) { + cprintf ("Proto Mask:\n"); + for (ProtoSetIndex = 0; ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); + for (ProtoWordNum = 0; ProtoWordNum < 2; + ProtoWordNum++, ProtoMask++) { + ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); + for (ProtoNum = 0; + ((ProtoNum < (PROTOS_PER_PROTO_SET >> 1)) + && (ActualProtoNum < NumProtos)); + ProtoNum++, ActualProtoNum++) + cprintf ("%1d", (((*ProtoMask) >> ProtoNum) & 1)); + cprintf ("\n"); + } + } + cprintf ("\n"); + } + + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + ProtoConfigs[ConfigNum] = 0; + + if (PrintProtoMatchesOn (Debug)) { + cprintf ("Proto Evidence:\n"); + for (ProtoSetIndex = 0; ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); + for (ProtoNum = 0; + ((ProtoNum < PROTOS_PER_PROTO_SET) + && (ActualProtoNum < NumProtos)); + ProtoNum++, ActualProtoNum++) { + cprintf ("P %3d =", ActualProtoNum); + Temp = 0; + UINT8Pointer = &(ProtoEvidence[ActualProtoNum][0]); + for (ProtoIndex = 0; + ProtoIndex < LengthForProtoId (ClassTemplate, + ActualProtoNum); + ProtoIndex++, UINT8Pointer++) { + cprintf (" %d", *UINT8Pointer); + Temp += *UINT8Pointer; + } + + cprintf (" = %6.4f%%\n", Temp / + 256.0 / LengthForProtoId (ClassTemplate, + ActualProtoNum)); + + ConfigWord = (ProtoSet->Protos[ProtoNum]).Configs[0]; + IntPointer = SumOfFeatureEvidence; + ConfigNum = 0; + while (ConfigWord) { + cprintf ("%5d", ConfigWord & 1 ? Temp : 0); + if (ConfigWord & 1) + ProtoConfigs[ConfigNum] += Temp; + IntPointer++; + ConfigNum++; + ConfigWord >>= 1; + } + cprintf ("\n"); + } + } + } + + if (PrintMatchSummaryOn (Debug)) { + cprintf ("Proto Error for Configurations:\n"); + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + cprintf (" %5.1f", + 100.0 * (1.0 - + ProtoConfigs[ConfigNum] / + LengthForConfigId (ClassTemplate, + ConfigNum) / 256.0)); + cprintf ("\n\n"); + } + + if (PrintProtoMatchesOn (Debug)) { + cprintf ("Proto Sum for Configurations:\n"); + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + cprintf (" %4.1f", ProtoConfigs[ConfigNum] / 256.0); + cprintf ("\n\n"); + + cprintf ("Proto Length for Configurations:\n"); + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + cprintf (" %4.1f", + (float) LengthForConfigId (ClassTemplate, ConfigNum)); + cprintf ("\n\n"); + } + +} + + +/*---------------------------------------------------------------------------*/ +void +IMDisplayProtoDebugInfo (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +int Debug) { + register UINT8 *UINT8Pointer; + register UINT32 ConfigWord; + register UINT16 ProtoNum; + register UINT16 ActualProtoNum; + PROTO_SET ProtoSet; + int ProtoSetIndex; + int ProtoIndex; + int NumProtos; + register int Temp; + + extern void *IntMatchWindow; + + if (IntMatchWindow == NULL) { + IntMatchWindow = c_create_window ("IntMatchWindow", 50, 200, + 520, 520, + -130.0, 130.0, -130.0, 130.0); + } + NumProtos = NumIntProtosIn (ClassTemplate); + + for (ProtoSetIndex = 0; ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); + for (ProtoNum = 0; + ((ProtoNum < PROTOS_PER_PROTO_SET) + && (ActualProtoNum < NumProtos)); ProtoNum++, ActualProtoNum++) { + /* Compute Average for Actual Proto */ + Temp = 0; + UINT8Pointer = &(ProtoEvidence[ActualProtoNum][0]); + for (ProtoIndex = LengthForProtoId (ClassTemplate, ActualProtoNum); + ProtoIndex > 0; ProtoIndex--, UINT8Pointer++) + Temp += *UINT8Pointer; + + Temp /= LengthForProtoId (ClassTemplate, ActualProtoNum); + + ConfigWord = (ProtoSet->Protos[ProtoNum]).Configs[0]; + ConfigWord &= *ConfigMask; + if (ConfigWord) + /* Update display for current proto */ + if (ClipMatchEvidenceOn (Debug)) { + if (Temp < AdaptProtoThresh) + DisplayIntProto (ClassTemplate, ActualProtoNum, + (Temp / 255.0)); + else + DisplayIntProto (ClassTemplate, ActualProtoNum, + (Temp / 255.0)); + } + else { + DisplayIntProto (ClassTemplate, ActualProtoNum, + (Temp / 255.0)); + } + } + } +} + + +/*---------------------------------------------------------------------------*/ +void IMDisplayFeatureDebugInfo(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + int Debug) { + static UINT8 FeatureEvidence[MAX_NUM_CONFIGS]; + static int SumOfFeatureEvidence[MAX_NUM_CONFIGS]; + static UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]; + int Feature; + register UINT8 *UINT8Pointer; + register int ConfigNum; + int NumConfigs; + register int Temp; + + IMClearTables(ClassTemplate, SumOfFeatureEvidence, ProtoEvidence); + + NumConfigs = NumIntConfigsIn (ClassTemplate); + for (Feature = 0; Feature < NumFeatures; Feature++) { + IMUpdateTablesForFeature (ClassTemplate, ProtoMask, ConfigMask, Feature, + &(Features[Feature]), FeatureEvidence, + SumOfFeatureEvidence, ProtoEvidence, 0); + + /* Find Best Evidence for Current Feature */ + Temp = 0; + UINT8Pointer = FeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, UINT8Pointer++) + if (*UINT8Pointer > Temp) + Temp = *UINT8Pointer; + + /* Update display for current feature */ + if (ClipMatchEvidenceOn (Debug)) { + if (Temp < AdaptFeatureThresh) + DisplayIntFeature (&(Features[Feature]), 0.0); + else + DisplayIntFeature (&(Features[Feature]), 1.0); + } + else { + DisplayIntFeature (&(Features[Feature]), (Temp / 255.0)); + } + } +} +#endif + +/*---------------------------------------------------------------------------*/ +void +IMUpdateSumOfProtoEvidences (INT_CLASS ClassTemplate, +BIT_VECTOR ConfigMask, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +INT16 NumFeatures) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Add sum of Proto Evidences into Sum Of Feature Evidence Array + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register UINT8 *UINT8Pointer; + register int *IntPointer; + register UINT32 ConfigWord; + int ProtoSetIndex; + register UINT16 ProtoNum; + PROTO_SET ProtoSet; + register int ProtoIndex; + int NumProtos; + UINT16 ActualProtoNum; + int Temp; + + NumProtos = NumIntProtosIn (ClassTemplate); + + for (ProtoSetIndex = 0; ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); + for (ProtoNum = 0; + ((ProtoNum < PROTOS_PER_PROTO_SET) + && (ActualProtoNum < NumProtos)); ProtoNum++, ActualProtoNum++) { + Temp = 0; + UINT8Pointer = &(ProtoEvidence[ActualProtoNum][0]); + for (ProtoIndex = LengthForProtoId (ClassTemplate, ActualProtoNum); + ProtoIndex > 0; ProtoIndex--, UINT8Pointer++) + Temp += *UINT8Pointer; + + ConfigWord = (ProtoSet->Protos[ProtoNum]).Configs[0]; + ConfigWord &= *ConfigMask; + IntPointer = SumOfFeatureEvidence; + while (ConfigWord) { + if (ConfigWord & 1) + *IntPointer += Temp; + IntPointer++; + ConfigWord >>= 1; + } + } + } +} + + +/*---------------------------------------------------------------------------*/ +void +PMNormalizeSumOfEvidences (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +INT16 NumFeatures, INT32 used_features) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Normalize Sum of Proto and Feature Evidence by dividing by + ** the sum of the Feature Lengths and the Proto Lengths for each + ** configuration. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register int *IntPointer; + register int ConfigNum; + int NumConfigs; + + NumConfigs = NumIntConfigsIn (ClassTemplate); + if (used_features <= 0) + used_features = 1; + + IntPointer = SumOfFeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, IntPointer++) + *IntPointer = (*IntPointer << 8) / used_features; +} + + +/*---------------------------------------------------------------------------*/ +void +IMNormalizeSumOfEvidences (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +INT16 NumFeatures, INT32 used_features) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Normalize Sum of Proto and Feature Evidence by dividing by + ** the sum of the Feature Lengths and the Proto Lengths for each + ** configuration. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register int *IntPointer; + register int ConfigNum; + int NumConfigs; + + NumConfigs = NumIntConfigsIn (ClassTemplate); + + IntPointer = SumOfFeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, IntPointer++) + *IntPointer = (*IntPointer << 8) / + (NumFeatures + LengthForConfigId (ClassTemplate, ConfigNum)); +} + + +/*---------------------------------------------------------------------------*/ +int +IMFindBestMatch (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT16 BlobLength, +UINT8 NormalizationFactor, INT_RESULT Result) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Find the best match for the current class and update the Result + ** with the configuration and match rating. + ** Return: + ** The best normalized sum of evidences + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register int *IntPointer; + register int ConfigNum; + register int NumConfigs; + register int BestMatch; + register int Best2Match; + + NumConfigs = NumIntConfigsIn (ClassTemplate); + + /* Find best match */ + BestMatch = 0; + Best2Match = 0; + IntPointer = SumOfFeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, IntPointer++) { + if (display_ratings > 1) + cprintf ("Config %d, rating=%d\n", ConfigNum, *IntPointer); + if (*IntPointer > BestMatch) { + if (BestMatch > 0) { + Result->Config2 = Result->Config; + Best2Match = BestMatch; + } + else + Result->Config2 = ConfigNum; + Result->Config = ConfigNum; + BestMatch = *IntPointer; + } + else if (*IntPointer > Best2Match) { + Result->Config2 = ConfigNum; + Best2Match = *IntPointer; + } + } + + /* Compute Certainty Rating */ + (*Result).Rating = ((65536.0 - BestMatch) / 65536.0 * BlobLength + + LocalMatcherMultiplier * NormalizationFactor / 256.0) / + (BlobLength + LocalMatcherMultiplier); + + return BestMatch; +} + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void IMDebugBestMatch(int BestMatch, + INT_RESULT Result, + UINT16 BlobLength, + UINT8 NormalizationFactor) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Find the best match for the current class and update the Result + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + cprintf ("Rating = %5.1f%% Best Config = %3d\n", + 100.0 * ((*Result).Rating), (int) ((*Result).Config)); + cprintf + ("Matcher Error = %5.1f%% Blob Length = %3d Weight = %4.1f%%\n", + 100.0 * (65536.0 - BestMatch) / 65536.0, (int) BlobLength, + 100.0 * BlobLength / (BlobLength + LocalMatcherMultiplier)); + cprintf + ("Char Norm Error = %5.1f%% Norm Strength = %3d Weight = %4.1f%%\n", + 100.0 * NormalizationFactor / 256.0, LocalMatcherMultiplier, + 100.0 * LocalMatcherMultiplier / (BlobLength + LocalMatcherMultiplier)); +} +#endif + +/*---------------------------------------------------------------------------*/ +void +HeapSort (int n, register INT16 ra[], register UINT8 rb[]) { +/* + ** Parameters: + ** n Number of elements to sort + ** ra Key array [1..n] + ** rb Index array [1..n] + ** Globals: + ** Operation: + ** Sort Key array in ascending order using heap sort + ** algorithm. Also sort Index array that is tied to + ** the key array. + ** Return: + ** Exceptions: none + ** History: Tue Feb 19 10:24:24 MST 1991, RWM, Created. + */ + register int i, rra, rrb; + int l, j, ir; + + l = (n >> 1) + 1; + ir = n; + for (;;) { + if (l > 1) { + rra = ra[--l]; + rrb = rb[l]; + } + else { + rra = ra[ir]; + rrb = rb[ir]; + ra[ir] = ra[1]; + rb[ir] = rb[1]; + if (--ir == 1) { + ra[1] = rra; + rb[1] = rrb; + return; + } + } + i = l; + j = l << 1; + while (j <= ir) { + if (j < ir && ra[j] < ra[j + 1]) + ++j; + if (rra < ra[j]) { + ra[i] = ra[j]; + rb[i] = rb[j]; + j += (i = j); + } + else + j = ir + 1; + } + ra[i] = rra; + rb[i] = rrb; + } +} diff --git a/classify/intmatcher.h b/classify/intmatcher.h new file mode 100644 index 0000000000..710ce57fda --- /dev/null +++ b/classify/intmatcher.h @@ -0,0 +1,240 @@ +/****************************************************************************** + ** Filename: intmatcher.h + ** Purpose: Interface to high level generic classifier routines. + ** Author: Robert Moss + ** History: Wed Feb 13 15:24:15 MST 1991, RWM, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef INTMATCHER_H +#define INTMATCHER_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "debug.h" +#include "intproto.h" +#include "cutoffs.h" + +typedef struct +{ + FLOAT32 Rating; + UINT8 Config; + UINT8 Config2; +} + + +INT_RESULT_STRUCT, *INT_RESULT; + +typedef struct +{ + FLOAT32 Rating; + FLOAT32 Rating2; + UINT32 config_mask; + CLASS_ID Class; +} + + +CP_RESULT_STRUCT; + +/*typedef CLASS_ID CLASS_PRUNER_RESULTS [MAX_NUM_CLASSES]; */ +typedef CP_RESULT_STRUCT CLASS_PRUNER_RESULTS[MAX_NUM_CLASSES]; + +typedef UINT8 CLASS_NORMALIZATION_ARRAY[MAX_NUM_CLASSES]; + +/*---------------------------------------------------------------------------- + Variables +-----------------------------------------------------------------------------*/ +extern int AdaptProtoThresh; +extern int AdaptFeatureThresh; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +int ClassPruner(INT_TEMPLATES IntTemplates, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + CLASS_NORMALIZATION_ARRAY NormalizationFactors, + CLASS_CUTOFF_ARRAY ExpectedNumFeatures, + CLASS_PRUNER_RESULTS Results, + int Debug); + +int feature_pruner(INT_TEMPLATES IntTemplates, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 NumClasses, + CLASS_PRUNER_RESULTS Results); + +int prune_configs(INT_TEMPLATES IntTemplates, + INT32 min_misses, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + CLASS_NORMALIZATION_ARRAY NormalizationFactors, + INT32 class_count, + UINT16 BlobLength, + CLASS_PRUNER_RESULTS Results, + int Debug); + +void PruningMatcher(INT_CLASS ClassTemplate, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 min_misses, + UINT8 NormalizationFactor, + INT_RESULT Result, + int Debug); + +void config_mask_to_proto_mask(INT_CLASS ClassTemplate, + BIT_VECTOR config_mask, + BIT_VECTOR proto_mask); + +void IntegerMatcher(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 min_misses, + UINT8 NormalizationFactor, + INT_RESULT Result, + int Debug); + +int FindGoodProtos(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + PROTO_ID *ProtoArray, + int Debug); + +int FindBadFeatures(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + FEATURE_ID *FeatureArray, + int Debug); + +void InitIntegerMatcher(); + +void InitIntegerMatcherVars(); + +void PrintIntMatcherStats(FILE *f); + +void SetProtoThresh(FLOAT32 Threshold); + +void SetFeatureThresh(FLOAT32 Threshold); + +void SetBaseLineMatch(); + +void SetCharNormMatch(); + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +void IMClearTables (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]); + +void IMClearFeatureEvidenceTable (UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int NumConfigs); + +void IMDebugConfiguration(INT_FEATURE FeatureNum, + UINT16 ActualProtoNum, + UINT8 Evidence, + BIT_VECTOR ConfigMask, + UINT32 ConfigWord); + +void IMDebugConfigurationSum(INT_FEATURE FeatureNum, + UINT8 *FeatureEvidence, + INT32 ConfigCount); + +void PMUpdateTablesForFeature (INT_CLASS ClassTemplate, +int FeatureNum, +INT_FEATURE Feature, +UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +int Debug); + +void IMUpdateTablesForFeature (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +int FeatureNum, +INT_FEATURE Feature, +UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +int Debug); + +#ifndef GRAPHICS_DISABLED +void IMDebugFeatureProtoError (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +INT16 NumFeatures, int Debug); + +void IMDisplayProtoDebugInfo (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +int Debug); + +void IMDisplayFeatureDebugInfo(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + int Debug); +#endif + +void IMUpdateSumOfProtoEvidences (INT_CLASS ClassTemplate, +BIT_VECTOR ConfigMask, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS] +[MAX_PROTO_INDEX], INT16 NumFeatures); + +void PMNormalizeSumOfEvidences (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +INT16 NumFeatures, INT32 used_features); + +void IMNormalizeSumOfEvidences (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +INT16 NumFeatures, INT32 used_features); + +int IMFindBestMatch (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT16 BlobLength, +UINT8 NormalizationFactor, INT_RESULT Result); + +#ifndef GRAPHICS_DISABLED +void IMDebugBestMatch(int BestMatch, + INT_RESULT Result, + UINT16 BlobLength, + UINT8 NormalizationFactor); +#endif + +void HeapSort (int n, register INT16 ra[], register UINT8 rb[]); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern int IntegerMatcherMultiplier; + +extern UINT32 EvidenceMultMask; +#endif diff --git a/classify/intproto.cpp b/classify/intproto.cpp new file mode 100644 index 0000000000..b922f76049 --- /dev/null +++ b/classify/intproto.cpp @@ -0,0 +1,1746 @@ +/****************************************************************************** + ** Filename: intproto.c + ** Purpose: Definition of data structures for integer protos. + ** Author: Dan Johnson + ** History: Thu Feb 7 14:38:16 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "intproto.h" +#include "picofeat.h" +#include "debug.h" +#include "mfoutline.h" +#include "emalloc.h" +#include "const.h" +#include "ndminx.h" +#include "adaptmatch.h" + +//extern GetPicoFeatureLength(); + +#include +#include +#include + +/* match debug display constants*/ +#define DISPLAY_OFFSET (0.5 * INT_CHAR_NORM_RANGE) +#define PROTO_PRUNER_SCALE (4.0) + +#define INT_DESCENDER (0.0 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) +#define INT_BASELINE (0.25 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) +#define INT_XHEIGHT (0.75 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) +#define INT_CAPHEIGHT (1.0 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) + +#define INT_XCENTER (0.5 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) +#define INT_YCENTER (0.5 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) +#define INT_XRADIUS (0.2 * INT_CHAR_NORM_RANGE) +#define INT_YRADIUS (0.2 * INT_CHAR_NORM_RANGE) +#define INT_MIN_X (- DISPLAY_OFFSET) +#define INT_MIN_Y (- DISPLAY_OFFSET) +#define INT_MAX_X ( DISPLAY_OFFSET) +#define INT_MAX_Y ( DISPLAY_OFFSET) +#define DOUBLE_OFFSET 0.095 + +/* define pad used to snap near horiz/vertical protos to horiz/vertical */ +#define HV_TOLERANCE (0.0025) /* approx 0.9 degrees */ + +typedef enum +{ StartSwitch, EndSwitch, LastSwitch } +SWITCH_TYPE; +#define MAX_NUM_SWITCHES 3 + +typedef struct +{ + SWITCH_TYPE Type; + INT8 X, Y; + INT16 YInit; + INT16 Delta; +} + + +FILL_SWITCH; + +typedef struct +{ + UINT8 NextSwitch; + UINT8 AngleStart, AngleEnd; + INT8 X; + INT16 YStart, YEnd; + INT16 StartDelta, EndDelta; + FILL_SWITCH Switch[MAX_NUM_SWITCHES]; +} + + +TABLE_FILLER; + +typedef struct +{ + INT8 X; + INT8 YStart, YEnd; + UINT8 AngleStart, AngleEnd; +} + + +FILL_SPEC; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* macro for performing circular increments of bucket indices */ +#define CircularIncrement(i,r) (((i) < (r) - 1)?((i)++):((i) = 0)) + +/* macro for mapping floats to ints without bounds checking */ +#define MapParam(P,O,N) (floor (((P) + (O)) * (N))) + +/*--------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------*/ +FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets); + +FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets); + +void DoFill(FILL_SPEC *FillSpec, + CLASS_PRUNER Pruner, + register UINT32 ClassMask, + register UINT32 ClassCount, + register UINT32 WordIndex); + +BOOL8 FillerDone(TABLE_FILLER *Filler); + +void FillPPCircularBits (UINT32 +ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], +int Bit, FLOAT32 Center, FLOAT32 Spread); + +void FillPPLinearBits (UINT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], +int Bit, FLOAT32 Center, FLOAT32 Spread); + +#ifndef GRAPHICS_DISABLED +CLASS_ID GetClassToDebug(const char *Prompt); +#endif + +void GetCPPadsForLevel(int Level, + FLOAT32 *EndPad, + FLOAT32 *SidePad, + FLOAT32 *AnglePad); + +C_COL GetMatchColorFor(FLOAT32 Evidence); + +void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill); + +void InitTableFiller(FLOAT32 EndPad, + FLOAT32 SidePad, + FLOAT32 AnglePad, + PROTO Proto, + TABLE_FILLER *Filler); + +#ifndef GRAPHICS_DISABLED +void RenderIntFeature(void *window, INT_FEATURE Feature, C_COL Color); + +void RenderIntProto(void *window, + INT_CLASS Class, + PROTO_ID ProtoId, + C_COL Color); +#endif + +int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/intproto.c +FLOAT32 BucketStart + _ARGS((int Bucket, + FLOAT32 Offset, + int NumBuckets)); + +FLOAT32 BucketEnd + _ARGS((int Bucket, + FLOAT32 Offset, + int NumBuckets)); + +void DoFill + _ARGS((FILL_SPEC *FillSpec, + CLASS_PRUNER Pruner, + UINT32 ClassMask, + UINT32 ClassCount, + UINT32 WordIndex)); + +BOOL8 FillerDone + _ARGS((TABLE_FILLER *Filler)); + +void FillPPCircularBits + _ARGS((UINT32 ParamTable [NUM_PP_BUCKETS ][WERDS_PER_PP_VECTOR ], + int Bit, + FLOAT32 Center, + FLOAT32 Spread)); + +void FillPPLinearBits + _ARGS((UINT32 ParamTable [NUM_PP_BUCKETS ][WERDS_PER_PP_VECTOR ], + int Bit, + FLOAT32 Center, + FLOAT32 Spread)); + +void GetCPPadsForLevel + _ARGS((int Level, + FLOAT32 *EndPad, + FLOAT32 *SidePad, + FLOAT32 *AnglePad)); + +C_COL GetMatchColorFor + _ARGS((FLOAT32 Evidence)); + +void GetNextFill + _ARGS((TABLE_FILLER *Filler, + FILL_SPEC *Fill)); + +void InitTableFiller + _ARGS((FLOAT32 EndPad, + FLOAT32 SidePad, + FLOAT32 AnglePad, + PROTO Proto, + TABLE_FILLER *Filler)); + +void RenderIntFeature + _ARGS((SHAPE_LIST ShapeList, + INT_FEATURE Feature, + char *Color)); + +void RenderIntProto + _ARGS((SHAPE_LIST ShapeList, + INT_CLASS Class, + PROTO_ID ProtoId, + char *Color)); + +int TruncateParam + _ARGS((FLOAT32 Param, + int Min, + int Max, + char *Id)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* control knobs */ +make_int_const (NumCPLevels, 3, MakeNumCPLevels); +make_float_const (CPAnglePadLoose, 45.0, MakeCPAnglePadLoose); +make_float_const (CPAnglePadMedium, 20.0, MakeCPAnglePadMedium); +make_float_const (CPAnglePadTight, 10.0, MakeCPAnglePadTight); +make_float_const (CPEndPadLoose, 0.5, MakeCPEndPadLoose); +make_float_const (CPEndPadMedium, 0.5, MakeCPEndPadMedium); +make_float_const (CPEndPadTight, 0.5, MakeCPEndPadTight); +make_float_const (CPSidePadLoose, 2.5, MakeCPSidePadLoose); +make_float_const (CPSidePadMedium, 1.2, MakeCPSidePadMedium); +make_float_const (CPSidePadTight, 0.6, MakeCPSidePadTight); +make_float_const (PPAnglePad, 45.0, MakePPAnglePad); +make_float_const (PPEndPad, 0.5, MakePPEndPad); +make_float_const (PPSidePad, 2.5, MakePPSidePad); + +/* global display lists used to display proto and feature match information*/ +void *IntMatchWindow = NULL; +//extern int LearningDebugLevel; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int AddIntClass(INT_TEMPLATES Templates, CLASS_ID ClassId, INT_CLASS Class) { +/* + ** Parameters: + ** Templates templates to add new class to + ** ClassId class id to associate new class with + ** Class class data structure to add to templates + ** Globals: none + ** Operation: This routine adds a new class structure to a set of + ** templates. + ** Return: The class index of the new class. + ** Exceptions: none + ** History: Mon Feb 11 11:52:08 1991, DSJ, Created. + */ + int Index; + int Pruner; + UINT32 *Word; + + assert (LegalClassId (ClassId)); + assert (UnusedClassIdIn (Templates, ClassId)); + + Index = NumClassesIn (Templates); + IndexForClassId (Templates, ClassId) = Index; + ClassIdForIndex (Templates, Index) = ClassId; + + NumClassesIn (Templates)++; + ClassForIndex (Templates, Index) = Class; + + if (NumClassesIn (Templates) > MaxNumClassesIn (Templates)) { + Pruner = NumClassPrunersIn (Templates); + NumClassPrunersIn (Templates)++; + Templates->ClassPruner[Pruner] = + (CLASS_PRUNER) Emalloc (sizeof (CLASS_PRUNER_STRUCT)); + + for (Word = (UINT32 *) (Templates->ClassPruner[Pruner]); + Word < (UINT32 *) (Templates->ClassPruner[Pruner]) + WERDS_PER_CP; + *Word++ = 0); + } + + return (Index); + +} /* AddIntClass */ + + +/*---------------------------------------------------------------------------*/ +int AddIntConfig(INT_CLASS Class) { +/* + ** Parameters: + ** Class class to add new configuration to + ** Globals: none + ** Operation: This routine returns the index of the next free config + ** in Class. + ** Return: Index of next free config. + ** Exceptions: none + ** History: Mon Feb 11 14:44:40 1991, DSJ, Created. + */ + int Index; + + assert (NumIntConfigsIn (Class) < MAX_NUM_CONFIGS); + + Index = NumIntConfigsIn (Class); + NumIntConfigsIn (Class)++; + LengthForConfigId (Class, Index) = 0; + return (Index); +} /* AddIntConfig */ + + +/*---------------------------------------------------------------------------*/ +int AddIntProto(INT_CLASS Class) { +/* + ** Parameters: + ** Class class to add new proto to + ** Globals: none + ** Operation: This routine allocates the next free proto in Class and + ** returns its index. + ** Return: Proto index of new proto. + ** Exceptions: none + ** History: Mon Feb 11 13:26:41 1991, DSJ, Created. + */ + int Index; + int ProtoSetId; + PROTO_SET ProtoSet; + INT_PROTO Proto; + register UINT32 *Word; + + if (NumIntProtosIn (Class) >= MAX_NUM_PROTOS) + return (NO_PROTO); + + Index = NumIntProtosIn (Class); + NumIntProtosIn (Class)++; + + if (NumIntProtosIn (Class) > MaxNumIntProtosIn (Class)) { + ProtoSetId = NumProtoSetsIn (Class); + NumProtoSetsIn (Class)++; + + ProtoSet = (PROTO_SET) Emalloc (sizeof (PROTO_SET_STRUCT)); + ProtoSetIn (Class, ProtoSetId) = ProtoSet; + for (Word = (UINT32 *) (ProtoPrunerFor (ProtoSet)); + Word < (UINT32 *) (ProtoPrunerFor (ProtoSet)) + WERDS_PER_PP; + *Word++ = 0); + + /* reallocate space for the proto lengths and install in class */ + Class->ProtoLengths = (UINT8 *) Erealloc (Class->ProtoLengths, + MaxNumIntProtosIn (Class) * + sizeof (UINT8)); + } + + /* initialize proto so its length is zero and it isn't in any configs */ + LengthForProtoId (Class, Index) = 0; + Proto = ProtoForProtoId (Class, Index); + for (Word = Proto->Configs; + Word < Proto->Configs + WERDS_PER_CONFIG_VEC; *Word++ = 0); + + return (Index); + +} /* AddIntProto */ + + +/*---------------------------------------------------------------------------*/ +void +AddProtoToClassPruner (PROTO Proto, CLASS_ID ClassId, INT_TEMPLATES Templates) +/* + ** Parameters: + ** Proto floating-pt proto to add to class pruner + ** ClassId class id corresponding to Proto + ** Templates set of templates containing class pruner + ** Globals: + ** NumCPLevels number of levels used in the class pruner + ** Operation: This routine adds Proto to the class pruning tables + ** for the specified class in Templates. + ** Return: none + ** Exceptions: none + ** History: Wed Feb 13 08:49:54 1991, DSJ, Created. + */ +#define MAX_LEVEL 2 +{ + CLASS_PRUNER Pruner; + UINT32 ClassMask; + UINT32 ClassCount; + CLASS_INDEX ClassIndex; + UINT32 WordIndex; + int Level; + FLOAT32 EndPad, SidePad, AnglePad; + TABLE_FILLER TableFiller; + FILL_SPEC FillSpec; + + ClassIndex = IndexForClassId (Templates, ClassId); + Pruner = CPrunerFor (Templates, ClassIndex); + WordIndex = CPrunerWordIndexFor (ClassIndex); + ClassMask = CPrunerMaskFor (MAX_LEVEL, ClassIndex); + + for (Level = NumCPLevels - 1; Level >= 0; Level--) { + GetCPPadsForLevel(Level, &EndPad, &SidePad, &AnglePad); + ClassCount = CPrunerMaskFor (Level, ClassIndex); + InitTableFiller(EndPad, SidePad, AnglePad, Proto, &TableFiller); + + while (!FillerDone (&TableFiller)) { + GetNextFill(&TableFiller, &FillSpec); + DoFill(&FillSpec, Pruner, ClassMask, ClassCount, WordIndex); + } + } +} /* AddProtoToClassPruner */ + + +/*---------------------------------------------------------------------------*/ +void AddProtoToProtoPruner(PROTO Proto, int ProtoId, INT_CLASS Class) { +/* + ** Parameters: + ** Proto floating-pt proto to be added to proto pruner + ** ProtoId id of proto + ** Class integer class that contains desired proto pruner + ** Globals: none + ** Operation: This routine updates the proto pruner lookup tables + ** for Class to include a new proto identified by ProtoId + ** and described by Proto. + ** Return: none + ** Exceptions: none + ** History: Fri Feb 8 13:07:19 1991, DSJ, Created. + */ + FLOAT32 Angle, X, Y, Length; + FLOAT32 Pad; + int Index; + PROTO_SET ProtoSet; + + if (ProtoId >= NumIntProtosIn (Class)) + cprintf ("AddProtoToProtoPruner:assert failed: %d < %d", + ProtoId, NumIntProtosIn (Class)); + assert (ProtoId < NumIntProtosIn (Class)); + + Index = IndexForProto (ProtoId); + ProtoSet = ProtoSetIn (Class, SetForProto (ProtoId)); + + Angle = ProtoAngle (Proto); + FillPPCircularBits (ProtoSet->ProtoPruner[PRUNER_ANGLE], Index, + Angle + ANGLE_SHIFT, PPAnglePad / 360.0); + + Angle *= 2.0 * PI; + Length = ProtoLength (Proto); + + X = ProtoX (Proto) + X_SHIFT; + Pad = max (fabs (cos (Angle)) * (Length / 2.0 + + PPEndPad * GetPicoFeatureLength ()), + fabs (sin (Angle)) * (PPSidePad * GetPicoFeatureLength ())); + + FillPPLinearBits (ProtoSet->ProtoPruner[PRUNER_X], Index, X, Pad); + + Y = ProtoY (Proto) + Y_SHIFT; + Pad = max (fabs (sin (Angle)) * (Length / 2.0 + + PPEndPad * GetPicoFeatureLength ()), + fabs (cos (Angle)) * (PPSidePad * GetPicoFeatureLength ())); + + FillPPLinearBits (ProtoSet->ProtoPruner[PRUNER_Y], Index, Y, Pad); + +} /* AddProtoToProtoPruner */ + + +/*---------------------------------------------------------------------------*/ +int BucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets) { +/* + ** Parameters: + ** Param parameter value to map into a bucket number + ** Offset amount to shift param before mapping it + ** NumBuckets number of buckets to map param into + ** Globals: none + ** Operation: This routine maps a parameter value into a bucket between + ** 0 and NumBuckets-1. Offset is added to the parameter + ** before mapping it. Values which map to buckets outside + ** the range are truncated to fit within the range. Mapping + ** is done by truncating rather than rounding. + ** Return: Bucket number corresponding to Param + Offset. + ** Exceptions: none + ** History: Thu Feb 14 13:24:33 1991, DSJ, Created. + */ + int Bucket; + + Bucket = (int) MapParam (Param, Offset, NumBuckets); + if (Bucket < 0) + Bucket = 0; + else if (Bucket >= NumBuckets) + Bucket = NumBuckets - 1; + return (Bucket); + +} /* BucketFor */ + + +/*---------------------------------------------------------------------------*/ +int CircBucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets) { +/* + ** Parameters: + ** Param parameter value to map into a circular bucket + ** Offset amount to shift param before mapping it + ** NumBuckets number of buckets to map param into + ** Globals: none + ** Operation: This routine maps a parameter value into a bucket between + ** 0 and NumBuckets-1. Offset is added to the parameter + ** before mapping it. Values which map to buckets outside + ** the range are wrapped to a new value in a circular fashion. + ** Mapping is done by truncating rather than rounding. + ** Return: Bucket number corresponding to Param + Offset. + ** Exceptions: none + ** History: Thu Feb 14 13:24:33 1991, DSJ, Created. + */ + int Bucket; + + Bucket = (int) MapParam (Param, Offset, NumBuckets); + if (Bucket < 0) + Bucket += NumBuckets; + else if (Bucket >= NumBuckets) + Bucket -= NumBuckets; + return (Bucket); + +} /* CircBucketFor */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void UpdateMatchDisplay() { +/* + ** Parameters: none + ** Globals: + ** FeatureShapes display list for features + ** ProtoShapes display list for protos + ** Operation: This routine clears the global feature and proto + ** display lists. + ** Return: none + ** Exceptions: none + ** History: Thu Mar 21 15:40:19 1991, DSJ, Created. + */ + if (IntMatchWindow != NULL) + c_make_current(IntMatchWindow); +} /* ClearMatchDisplay */ +#endif + +/*---------------------------------------------------------------------------*/ +void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class) { +/* + ** Parameters: + ** Config config to be added to class + ** ConfigId id to be used for new config + ** Class class to add new config to + ** Globals: none + ** Operation: This operation updates the config vectors of all protos + ** in Class to indicate that the protos with 1's in Config + ** belong to a new configuration identified by ConfigId. + ** It is assumed that the length of the Config bit vector is + ** equal to the number of protos in Class. + ** Return: none + ** Exceptions: none + ** History: Mon Feb 11 14:57:31 1991, DSJ, Created. + */ + int ProtoId; + INT_PROTO Proto; + int TotalLength; + + for (ProtoId = 0, TotalLength = 0; + ProtoId < NumIntProtosIn (Class); ProtoId++) + if (test_bit (Config, ProtoId)) { + Proto = ProtoForProtoId (Class, ProtoId); + SET_BIT (Proto->Configs, ConfigId); + TotalLength += LengthForProtoId (Class, ProtoId); + } + LengthForConfigId (Class, ConfigId) = TotalLength; +} /* ConvertConfig */ + + +/*---------------------------------------------------------------------------*/ +void ConvertProto(PROTO Proto, int ProtoId, INT_CLASS Class) { +/* + ** Parameters: + ** Proto floating-pt proto to be converted to integer format + ** ProtoId id of proto + ** Class integer class to add converted proto to + ** Globals: none + ** Operation: This routine converts Proto to integer format and + ** installs it as ProtoId in Class. + ** Return: none + ** Exceptions: none + ** History: Fri Feb 8 11:22:43 1991, DSJ, Created. + */ + INT_PROTO P; + FLOAT32 Param; + + assert (ProtoId < NumIntProtosIn (Class)); + + P = ProtoForProtoId (Class, ProtoId); + + Param = CoefficientA (Proto) * 128; + P->A = TruncateParam (Param, -128, 127, NULL); + + Param = -CoefficientB (Proto) * 256; + P->B = TruncateParam (Param, 0, 255, NULL); + + Param = CoefficientC (Proto) * 128; + P->C = TruncateParam (Param, -128, 127, NULL); + + Param = ProtoAngle (Proto) * 256; + if (Param < 0 || Param >= 256) + P->Angle = 0; + else + P->Angle = (UINT8) Param; + + /* round proto length to nearest integer number of pico-features */ + Param = (ProtoLength (Proto) / GetPicoFeatureLength ()) + 0.5; + LengthForProtoId (Class, ProtoId) = TruncateParam (Param, 1, 255, NULL); + if (LearningDebugLevel >= 2) + cprintf ("Converted ffeat to (A=%d,B=%d,C=%d,L=%d)", + P->A, P->B, P->C, LengthForProtoId (Class, ProtoId)); +} /* ConvertProto */ + + +/*---------------------------------------------------------------------------*/ +INT_TEMPLATES CreateIntTemplates(CLASSES FloatProtos) { +/* + ** Parameters: + ** FloatProtos prototypes in old floating pt format + ** Globals: none + ** Operation: This routine converts from the old floating point format + ** to the new integer format. + ** Return: New set of training templates in integer format. + ** Exceptions: none + ** History: Thu Feb 7 14:40:42 1991, DSJ, Created. + */ + INT_TEMPLATES IntTemplates; + CLASS_TYPE FClass; + INT_CLASS IClass; + int ClassId; + int ProtoId; + int ConfigId; + + IntTemplates = NewIntTemplates (); + + for (ClassId = 0; ClassId < NUMBER_OF_CLASSES; ClassId++) { + FClass = &(FloatProtos[ClassId]); + if (NumProtosIn (FClass) > 0) { + assert (UnusedClassIdIn (IntTemplates, ClassId)); + + IClass = NewIntClass (NumProtosIn (FClass), NumConfigsIn (FClass)); + AddIntClass(IntTemplates, ClassId, IClass); + + for (ProtoId = 0; ProtoId < NumProtosIn (FClass); ProtoId++) { + AddIntProto(IClass); + ConvertProto (ProtoIn (FClass, ProtoId), ProtoId, IClass); + AddProtoToProtoPruner (ProtoIn (FClass, ProtoId), ProtoId, + IClass); + AddProtoToClassPruner (ProtoIn (FClass, ProtoId), ClassId, + IntTemplates); + } + + for (ConfigId = 0; ConfigId < NumConfigsIn (FClass); ConfigId++) { + AddIntConfig(IClass); + ConvertConfig (ConfigIn (FClass, ConfigId), ConfigId, IClass); + } + } + } + return (IntTemplates); +} /* CreateIntTemplates */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void DisplayIntFeature(INT_FEATURE Feature, FLOAT32 Evidence) { +/* + ** Parameters: + ** Feature pico-feature to be displayed + ** Evidence best evidence for this feature (0-1) + ** Globals: + ** FeatureShapes global display list for features + ** Operation: This routine renders the specified feature into a + ** global display list. + ** Return: none + ** Exceptions: none + ** History: Thu Mar 21 14:45:04 1991, DSJ, Created. + */ + C_COL Color; + + Color = GetMatchColorFor (Evidence); + RenderIntFeature(IntMatchWindow, Feature, Color); +} /* DisplayIntFeature */ + + +/*---------------------------------------------------------------------------*/ +void DisplayIntProto(INT_CLASS Class, PROTO_ID ProtoId, FLOAT32 Evidence) { +/* + ** Parameters: + ** Class class to take proto from + ** ProtoId id of proto in Class to be displayed + ** Evidence total evidence for proto (0-1) + ** Globals: + ** ProtoShapes global display list for protos + ** Operation: This routine renders the specified proto into a + ** global display list. + ** Return: none + ** Exceptions: none + ** History: Thu Mar 21 14:45:04 1991, DSJ, Created. + */ + C_COL Color; + + Color = GetMatchColorFor (Evidence); + RenderIntProto(IntMatchWindow, Class, ProtoId, Color); + +} /* DisplayIntProto */ +#endif + +/*---------------------------------------------------------------------------*/ +void InitIntProtoVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Initialize the control variables for the integer proto + ** routines. + ** Return: none + ** Exceptions: none + ** History: Tue Feb 12 08:04:34 1991, DSJ, Created. + */ + MakeNumCPLevels(); + MakeCPAnglePadLoose(); + MakeCPAnglePadMedium(); + MakeCPAnglePadTight(); + MakeCPEndPadLoose(); + MakeCPEndPadMedium(); + MakeCPEndPadTight(); + MakeCPSidePadLoose(); + MakeCPSidePadMedium(); + MakeCPSidePadTight(); + MakePPAnglePad(); + MakePPEndPad(); + MakePPSidePad(); +} /* InitIntProtoVars */ + + +/*---------------------------------------------------------------------------*/ +INT_CLASS NewIntClass(int MaxNumProtos, int MaxNumConfigs) { +/* + ** Parameters: + ** MaxNumProtos number of protos to allocate space for + ** MaxNumConfigs number of configs to allocate space for + ** Globals: none + ** Operation: This routine creates a new integer class data structure + ** and returns it. Sufficient space is allocated + ** to handle the specified number of protos and configs. + ** Return: New class created. + ** Exceptions: none + ** History: Fri Feb 8 10:51:23 1991, DSJ, Created. + */ + INT_CLASS Class; + PROTO_SET ProtoSet; + int i; + register UINT32 *Word; + + assert (MaxNumConfigs <= MAX_NUM_CONFIGS); + + Class = (INT_CLASS) Emalloc (sizeof (INT_CLASS_STRUCT)); + NumProtoSetsIn (Class) = ((MaxNumProtos + PROTOS_PER_PROTO_SET - 1) / + PROTOS_PER_PROTO_SET); + + assert (NumProtoSetsIn (Class) <= MAX_NUM_PROTO_SETS); + + NumIntProtosIn (Class) = 0; + NumIntConfigsIn (Class) = 0; + + for (i = 0; i < NumProtoSetsIn (Class); i++) { + /* allocate space for a proto set, install in class, and initialize */ + ProtoSet = (PROTO_SET) Emalloc (sizeof (PROTO_SET_STRUCT)); + ProtoSetIn (Class, i) = ProtoSet; + for (Word = (UINT32 *) (ProtoPrunerFor (ProtoSet)); + Word < (UINT32 *) (ProtoPrunerFor (ProtoSet)) + WERDS_PER_PP; + *Word++ = 0); + + /* allocate space for the proto lengths and install in class */ + } + Class->ProtoLengths = (UINT8 *) Emalloc (MaxNumIntProtosIn (Class) * + sizeof (UINT8)); + + return (Class); + +} /* NewIntClass */ + + +/*-------------------------------------------------------------------------*/ +void free_int_class( /*class to free */ + INT_CLASS int_class) { + int i; + + for (i = 0; i < NumProtoSetsIn (int_class); i++) { + Efree (ProtoSetIn (int_class, i)); + } + Efree (int_class->ProtoLengths); + Efree(int_class); +} + + +/*---------------------------------------------------------------------------*/ +INT_TEMPLATES NewIntTemplates() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine allocates a new set of integer templates + ** initialized to hold 0 classes. + ** Return: The integer templates created. + ** Exceptions: none + ** History: Fri Feb 8 08:38:51 1991, DSJ, Created. + */ + INT_TEMPLATES T; + int i; + + T = (INT_TEMPLATES) Emalloc (sizeof (INT_TEMPLATES_STRUCT)); + NumClassesIn (T) = 0; + NumClassPrunersIn (T) = 0; + + /* initialize mapping tables */ + for (i = 0; i <= MAX_CLASS_ID; i++) + IndexForClassId (T, i) = ILLEGAL_CLASS; + for (i = 0; i < MAX_NUM_CLASSES; i++) + ClassIdForIndex (T, i) = NO_CLASS; + + return (T); + +} /* NewIntTemplates */ + + +/*---------------------------------------------------------------------------*/ +void free_int_templates(INT_TEMPLATES templates) { + int i; + + for (i = 0; i < NumClassesIn (templates); i++) + free_int_class (ClassForIndex (templates, i)); + for (i = 0; i < NumClassPrunersIn (templates); i++) + Efree (templates->ClassPruner[i]); + Efree(templates); +} + + +/*---------------------------------------------------------------------------*/ +INT_TEMPLATES ReadIntTemplates(FILE *File, BOOL8 swap) { +/* + ** Parameters: + ** File open file to read templates from + ** Globals: none + ** Operation: This routine reads a set of integer templates from + ** File. File must already be open and must be in the + ** correct binary format. + ** Return: Pointer to integer templates read from File. + ** Exceptions: none + ** History: Wed Feb 27 11:48:46 1991, DSJ, Created. + */ + int i, j, x, y, z; + int nread; + INT_TEMPLATES Templates; + CLASS_PRUNER Pruner; + INT_CLASS Class; + UINT8 *Lengths; + PROTO_SET ProtoSet; + + /* first read the high level template struct */ + Templates = NewIntTemplates (); + // Read Templates in parts for 64 bit compatibility. + if (fread(&Templates->NumClasses, sizeof(int), 1, File) != 1 || + fread(&Templates->NumClassPruners, sizeof(int), 1, File) != 1) + cprintf ("Bad read of inttemp!\n"); + for (i = 0; i <= MAX_CLASS_ID; ++i) { + if (fread(&Templates->IndexFor[i], sizeof(CLASS_INDEX), 1, File) != 1) + cprintf("Bad read of inttemp!\n"); + } + for (i = 0; i < MAX_NUM_CLASSES; ++i) { + if (fread(&Templates->ClassIdFor[i], sizeof(CLASS_ID), 1, File) != 1) + cprintf("Bad read of inttemp!\n"); + } + for (i = 0; i < MAX_NUM_CLASSES + MAX_NUM_CLASS_PRUNERS; ++i) { + int junk; + if (fread(&junk, sizeof(junk), 1, File) != 1) + cprintf("Bad read of inttemp!\n"); + } + // Swap status is determined automatically. + swap = Templates->NumClassPruners < 0 || + Templates->NumClassPruners > MAX_NUM_CLASS_PRUNERS; + if (swap) { + reverse32 (&Templates->NumClassPruners); + reverse32 (&Templates->NumClasses); + for (i = 0; i < MAX_CLASS_ID + 1; i++) + reverse16 (&Templates->IndexFor[i]); + } + + /* then read in the class pruners */ + for (i = 0; i < NumClassPrunersIn (Templates); i++) { + Pruner = (CLASS_PRUNER) Emalloc (sizeof (CLASS_PRUNER_STRUCT)); + if ((nread = + fread ((char *) Pruner, 1, sizeof (CLASS_PRUNER_STRUCT), + File)) != sizeof (CLASS_PRUNER_STRUCT)) + cprintf ("Bad read of inttemp!\n"); + if (swap) { + for (j = 0; j < NUM_CP_BUCKETS; j++) { + for (x = 0; x < NUM_CP_BUCKETS; x++) { + for (y = 0; y < NUM_CP_BUCKETS; y++) { + for (z = 0; z < WERDS_PER_CP_VECTOR; z++) { + reverse32 (&Pruner[j][x][y][z]); + } + } + } + } + } + Templates->ClassPruner[i] = Pruner; + } + + /* then read in each class */ + for (i = 0; i < NumClassesIn (Templates); i++) { + /* first read in the high level struct for the class */ + Class = (INT_CLASS) Emalloc (sizeof (INT_CLASS_STRUCT)); + if (fread(&Class->NumProtos, sizeof(Class->NumProtos), 1, File) != 1 || + fread(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File) != 1 || + fread(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File) != 1) + cprintf ("Bad read of inttemp!\n"); + for (j = 0; j <= MAX_NUM_PROTO_SETS; ++j) { + int junk; + if (fread(&junk, sizeof(junk), 1, File) != 1) + cprintf ("Bad read of inttemp!\n"); + } + for (j = 0; j < MAX_NUM_CONFIGS; ++j) { + if (fread(&Class->ConfigLengths[j], sizeof(UINT16), 1, File) != 1) + cprintf ("Bad read of inttemp!\n"); + } + if (swap) { + reverse16 (&Class->NumProtos); + for (j = 0; j < MAX_NUM_CONFIGS; j++) + reverse16 (&Class->ConfigLengths[j]); + } + ClassForIndex (Templates, i) = Class; + + /* then read in the proto lengths */ + Lengths = (UINT8 *) Emalloc (sizeof (UINT8) * + MaxNumIntProtosIn (Class)); + if ((nread = fread ((char *) Lengths, sizeof (UINT8), + MaxNumIntProtosIn (Class), + File)) != MaxNumIntProtosIn (Class)) + cprintf ("Bad read of inttemp!\n"); + Class->ProtoLengths = Lengths; + + /* then read in the proto sets */ + for (j = 0; j < NumProtoSetsIn (Class); j++) { + ProtoSet = (PROTO_SET) Emalloc (sizeof (PROTO_SET_STRUCT)); + if ((nread = + fread ((char *) ProtoSet, 1, sizeof (PROTO_SET_STRUCT), + File)) != sizeof (PROTO_SET_STRUCT)) + cprintf ("Bad read of inttemp!\n"); + if (swap) { + for (x = 0; x < NUM_PP_PARAMS; x++) + for (y = 0; y < NUM_PP_BUCKETS; y++) + for (z = 0; z < WERDS_PER_PP_VECTOR; z++) + reverse32 (&ProtoSet->ProtoPruner[x][y][z]); + for (x = 0; x < PROTOS_PER_PROTO_SET; x++) + for (y = 0; y < WERDS_PER_CONFIG_VEC; y++) + reverse32 (&ProtoSet->Protos[x].Configs[y]); + } + ProtoSetIn (Class, j) = ProtoSet; + } + } + return (Templates); +} /* ReadIntTemplates */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void ShowMatchDisplay() { +/* + ** Parameters: none + ** Globals: + ** FeatureShapes display list containing feature matches + ** ProtoShapes display list containing proto matches + ** Operation: This routine sends the shapes in the global display + ** lists to the match debugger window. + ** Return: none + ** Exceptions: none + ** History: Thu Mar 21 15:47:33 1991, DSJ, Created. + */ + void *window; + /* Size of drawable */ + if (IntMatchWindow == NULL) { + IntMatchWindow = c_create_window ("IntMatchWindow", 50, 200, + 520, 520, + -130.0, 130.0, -130.0, 130.0); + } + else + c_clear_window(IntMatchWindow); + + window = IntMatchWindow; + c_line_color_index(window, Grey); + /* Default size of drawing */ + if (NormMethod == baseline) { + c_move (window, -1000.0, INT_BASELINE); + c_draw (window, 1000.0, INT_BASELINE); + c_move (window, -1000.0, INT_DESCENDER); + c_draw (window, 1000.0, INT_DESCENDER); + c_move (window, -1000.0, INT_XHEIGHT); + c_draw (window, 1000.0, INT_XHEIGHT); + c_move (window, -1000.0, INT_CAPHEIGHT); + c_draw (window, 1000.0, INT_CAPHEIGHT); + c_move (window, INT_MIN_X, -1000.0); + c_draw (window, INT_MIN_X, 1000.0); + c_move (window, INT_MAX_X, -1000.0); + c_draw (window, INT_MAX_X, 1000.0); + } + else { + c_move (window, INT_XCENTER - INT_XRADIUS, INT_YCENTER - INT_YRADIUS); + c_draw (window, INT_XCENTER + INT_XRADIUS, INT_YCENTER - INT_YRADIUS); + c_move (window, INT_XCENTER - INT_XRADIUS, INT_YCENTER + INT_YRADIUS); + c_draw (window, INT_XCENTER + INT_XRADIUS, INT_YCENTER + INT_YRADIUS); + c_move (window, INT_XCENTER - INT_XRADIUS, INT_YCENTER - INT_YRADIUS); + c_draw (window, INT_XCENTER - INT_XRADIUS, INT_YCENTER + INT_YRADIUS); + c_move (window, INT_XCENTER + INT_XRADIUS, INT_YCENTER - INT_YRADIUS); + c_draw (window, INT_XCENTER + INT_XRADIUS, INT_YCENTER + INT_YRADIUS); + c_move(window, INT_MIN_X, INT_MIN_Y); + c_draw(window, INT_MIN_X, INT_MAX_Y); + c_move(window, INT_MIN_X, INT_MIN_Y); + c_draw(window, INT_MAX_X, INT_MIN_Y); + c_move(window, INT_MAX_X, INT_MAX_Y); + c_draw(window, INT_MIN_X, INT_MAX_Y); + c_move(window, INT_MAX_X, INT_MAX_Y); + c_draw(window, INT_MAX_X, INT_MIN_Y); + } +} /* ShowMatchDisplay */ +#endif + +/*---------------------------------------------------------------------------*/ +void WriteIntTemplates(FILE *File, INT_TEMPLATES Templates) { +/* + ** Parameters: + ** File open file to write templates to + ** Templates templates to save into File + ** Globals: none + ** Operation: This routine writes Templates to File. The format + ** is an efficient binary format. File must already be open + ** for writing. + ** Return: none + ** Exceptions: none + ** History: Wed Feb 27 11:48:46 1991, DSJ, Created. + */ + int i, j; + INT_CLASS Class; + + /* first write the high level template struct */ + fwrite ((char *) Templates, sizeof (INT_TEMPLATES_STRUCT), 1, File); + + /* then write out the class pruners */ + for (i = 0; i < NumClassPrunersIn (Templates); i++) + fwrite ((char *) (Templates->ClassPruner[i]), + sizeof (CLASS_PRUNER_STRUCT), 1, File); + + /* then write out each class */ + for (i = 0; i < NumClassesIn (Templates); i++) { + Class = ClassForIndex (Templates, i); + + /* first write out the high level struct for the class */ + fwrite ((char *) Class, sizeof (INT_CLASS_STRUCT), 1, File); + + /* then write out the proto lengths */ + fwrite ((char *) (Class->ProtoLengths), sizeof (UINT8), + MaxNumIntProtosIn (Class), File); + + /* then write out the proto sets */ + for (j = 0; j < NumProtoSetsIn (Class); j++) + fwrite ((char *) ProtoSetIn (Class, j), + sizeof (PROTO_SET_STRUCT), 1, File); + } +} /* WriteIntTemplates */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets) { +/* + ** Parameters: + ** Bucket bucket whose start is to be computed + ** Offset offset used to map params to buckets + ** NumBuckets total number of buckets + ** Globals: none + ** Operation: This routine returns the parameter value which + ** corresponds to the beginning of the specified bucket. + ** The bucket number should have been generated using the + ** BucketFor() function with parameters Offset and NumBuckets. + ** Return: Param value corresponding to start position of Bucket. + ** Exceptions: none + ** History: Thu Feb 14 13:24:33 1991, DSJ, Created. + */ + return (((FLOAT32) Bucket / NumBuckets) - Offset); + +} /* BucketStart */ + + +/*---------------------------------------------------------------------------*/ +FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets) { +/* + ** Parameters: + ** Bucket bucket whose end is to be computed + ** Offset offset used to map params to buckets + ** NumBuckets total number of buckets + ** Globals: none + ** Operation: This routine returns the parameter value which + ** corresponds to the end of the specified bucket. + ** The bucket number should have been generated using the + ** BucketFor() function with parameters Offset and NumBuckets. + ** Return: Param value corresponding to end position of Bucket. + ** Exceptions: none + ** History: Thu Feb 14 13:24:33 1991, DSJ, Created. + */ + return (((FLOAT32) (Bucket + 1) / NumBuckets) - Offset); +} /* BucketEnd */ + + +/*---------------------------------------------------------------------------*/ +void DoFill(FILL_SPEC *FillSpec, + CLASS_PRUNER Pruner, + register UINT32 ClassMask, + register UINT32 ClassCount, + register UINT32 WordIndex) { +/* + ** Parameters: + ** FillSpec specifies which bits to fill in pruner + ** Pruner class pruner to be filled + ** ClassMask indicates which bits to change in each word + ** ClassCount indicates what to change bits to + ** WordIndex indicates which word to change + ** Globals: none + ** Operation: This routine fills in the section of a class pruner + ** corresponding to a single x value for a single proto of + ** a class. + ** Return: none + ** Exceptions: none + ** History: Tue Feb 19 11:11:29 1991, DSJ, Created. + */ + register int X, Y, Angle; + register UINT32 OldWord; + + X = FillSpec->X; + if (X < 0) + X = 0; + if (X >= NUM_CP_BUCKETS) + X = NUM_CP_BUCKETS - 1; + + if (FillSpec->YStart < 0) + FillSpec->YStart = 0; + if (FillSpec->YEnd >= NUM_CP_BUCKETS) + FillSpec->YEnd = NUM_CP_BUCKETS - 1; + + for (Y = FillSpec->YStart; Y <= FillSpec->YEnd; Y++) + for (Angle = FillSpec->AngleStart; + TRUE; CircularIncrement (Angle, NUM_CP_BUCKETS)) { + OldWord = Pruner[X][Y][Angle][WordIndex]; + if (ClassCount > (OldWord & ClassMask)) { + OldWord &= ~ClassMask; + OldWord |= ClassCount; + Pruner[X][Y][Angle][WordIndex] = OldWord; + } + if (Angle == FillSpec->AngleEnd) + break; + } +} /* DoFill */ + + +/*---------------------------------------------------------------------------*/ +BOOL8 FillerDone(TABLE_FILLER *Filler) { +/* + ** Parameters: + ** Filler table filler to check if done + ** Globals: none + ** Operation: Return TRUE if the specified table filler is done, i.e. + ** if it has no more lines to fill. + ** Return: TRUE if no more lines to fill, FALSE otherwise. + ** Exceptions: none + ** History: Tue Feb 19 10:08:05 1991, DSJ, Created. + */ + FILL_SWITCH *Next; + + Next = &(Filler->Switch[Filler->NextSwitch]); + + if (Filler->X > Next->X && Next->Type == LastSwitch) + return (TRUE); + else + return (FALSE); + +} /* FillerDone */ + + +/*---------------------------------------------------------------------------*/ +void +FillPPCircularBits (UINT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], +int Bit, FLOAT32 Center, FLOAT32 Spread) { +/* + ** Parameters: + ** ParamTable table of bit vectors, one per param bucket + ** Bit bit position in vectors to be filled + ** Center center of filled area + ** Spread spread of filled area + ** Globals: none + ** Operation: This routine sets Bit in each bit vector whose + ** bucket lies within the range Center +- Spread. The fill + ** is done for a circular dimension, i.e. bucket 0 is adjacent + ** to the last bucket. It is assumed that Center and Spread + ** are expressed in a circular coordinate system whose range + ** is 0 to 1. + ** Return: none + ** Exceptions: none + ** History: Tue Oct 16 09:26:54 1990, DSJ, Created. + */ + int i, FirstBucket, LastBucket; + + if (Spread > 0.5) + Spread = 0.5; + + FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS); + if (FirstBucket < 0) + FirstBucket += NUM_PP_BUCKETS; + + LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS); + if (LastBucket >= NUM_PP_BUCKETS) + LastBucket -= NUM_PP_BUCKETS; + if (LearningDebugLevel >= 2) + cprintf ("Circular fill from %d to %d", FirstBucket, LastBucket); + for (i = FirstBucket; TRUE; CircularIncrement (i, NUM_PP_BUCKETS)) { + SET_BIT (ParamTable[i], Bit); + + /* exit loop after we have set the bit for the last bucket */ + if (i == LastBucket) + break; + } + +} /* FillPPCircularBits */ + + +/*---------------------------------------------------------------------------*/ +void +FillPPLinearBits (UINT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], +int Bit, FLOAT32 Center, FLOAT32 Spread) { +/* + ** Parameters: + ** ParamTable table of bit vectors, one per param bucket + ** Bit bit number being filled + ** Center center of filled area + ** Spread spread of filled area + ** Globals: none + ** Operation: This routine sets Bit in each bit vector whose + ** bucket lies within the range Center +- Spread. The fill + ** is done for a linear dimension, i.e. there is no wrap-around + ** for this dimension. It is assumed that Center and Spread + ** are expressed in a linear coordinate system whose range + ** is approximately 0 to 1. Values outside this range will + ** be clipped. + ** Return: none + ** Exceptions: none + ** History: Tue Oct 16 09:26:54 1990, DSJ, Created. + */ + int i, FirstBucket, LastBucket; + + FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS); + if (FirstBucket < 0) + FirstBucket = 0; + + LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS); + if (LastBucket >= NUM_PP_BUCKETS) + LastBucket = NUM_PP_BUCKETS - 1; + + if (LearningDebugLevel >= 2) + cprintf ("Linear fill from %d to %d", FirstBucket, LastBucket); + for (i = FirstBucket; i <= LastBucket; i++) + SET_BIT (ParamTable[i], Bit); + +} /* FillPPLinearBits */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +CLASS_ID GetClassToDebug(const char *Prompt) { +/* + ** Parameters: + ** Prompt prompt to print while waiting for input from window + ** Globals: none + ** Operation: This routine prompts the user with Prompt and waits + ** for the user to enter something in the debug window. + ** Return: Character entered in the debug window. + ** Exceptions: none + ** History: Thu Mar 21 16:55:13 1991, DSJ, Created. + */ + return window_wait (IntMatchWindow); + +} /* GetClassToDebug */ +#endif + +/*---------------------------------------------------------------------------*/ +void GetCPPadsForLevel(int Level, + FLOAT32 *EndPad, + FLOAT32 *SidePad, + FLOAT32 *AnglePad) { +/* + ** Parameters: + ** Level "tightness" level to return pads for + ** EndPad place to put end pad for Level + ** SidePad place to put side pad for Level + ** AnglePad place to put angle pad for Level + ** Globals: none + ** Operation: This routine copies the appropriate global pad variables + ** into EndPad, SidePad, and AnglePad. This is a kludge used + ** to get around the fact that global control variables cannot + ** be arrays. If the specified level is illegal, the tightest + ** possible pads are returned. + ** Return: none (results are returned in EndPad, SidePad, and AnglePad. + ** Exceptions: none + ** History: Thu Feb 14 08:26:49 1991, DSJ, Created. + */ + switch (Level) { + case 0: + *EndPad = CPEndPadLoose * GetPicoFeatureLength (); + *SidePad = CPSidePadLoose * GetPicoFeatureLength (); + *AnglePad = CPAnglePadLoose / 360.0; + break; + + case 1: + *EndPad = CPEndPadMedium * GetPicoFeatureLength (); + *SidePad = CPSidePadMedium * GetPicoFeatureLength (); + *AnglePad = CPAnglePadMedium / 360.0; + break; + + case 2: + *EndPad = CPEndPadTight * GetPicoFeatureLength (); + *SidePad = CPSidePadTight * GetPicoFeatureLength (); + *AnglePad = CPAnglePadTight / 360.0; + break; + + default: + *EndPad = CPEndPadTight * GetPicoFeatureLength (); + *SidePad = CPSidePadTight * GetPicoFeatureLength (); + *AnglePad = CPAnglePadTight / 360.0; + break; + } + if (*AnglePad > 0.5) + *AnglePad = 0.5; + +} /* GetCPPadsForLevel */ + + +/*---------------------------------------------------------------------------*/ +C_COL GetMatchColorFor(FLOAT32 Evidence) { +/* + ** Parameters: + ** Evidence evidence value to return color for + ** Globals: none + ** Operation: + ** Return: Color which corresponds to specified Evidence value. + ** Exceptions: none + ** History: Thu Mar 21 15:24:52 1991, DSJ, Created. + */ + + assert (Evidence >= 0.0); + assert (Evidence <= 1.0); + + if (Evidence >= 0.90) + return White; + else if (Evidence >= 0.75) + return Green; + else if (Evidence >= 0.50) + return Red; + else + return Blue; +} /* GetMatchColorFor */ + + +/*---------------------------------------------------------------------------*/ +void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill) { +/* + ** Parameters: + ** Filler filler to get next fill spec from + ** Fill place to put spec for next fill + ** Globals: none + ** Operation: This routine returns (in Fill) the specification of + ** the next line to be filled from Filler. FillerDone() should + ** always be called before GetNextFill() to ensure that we + ** do not run past the end of the fill table. + ** Return: none (results are returned in Fill) + ** Exceptions: none + ** History: Tue Feb 19 10:17:42 1991, DSJ, Created. + */ + FILL_SWITCH *Next; + + /* compute the fill assuming no switches will be encountered */ + Fill->AngleStart = Filler->AngleStart; + Fill->AngleEnd = Filler->AngleEnd; + Fill->X = Filler->X; + Fill->YStart = Filler->YStart >> 8; + Fill->YEnd = Filler->YEnd >> 8; + + /* update the fill info and the filler for ALL switches at this X value */ + Next = &(Filler->Switch[Filler->NextSwitch]); + while (Filler->X >= Next->X) { + Fill->X = Filler->X = Next->X; + if (Next->Type == StartSwitch) { + Fill->YStart = Next->Y; + Filler->StartDelta = Next->Delta; + Filler->YStart = Next->YInit; + } + else if (Next->Type == EndSwitch) { + Fill->YEnd = Next->Y; + Filler->EndDelta = Next->Delta; + Filler->YEnd = Next->YInit; + } + else { /* Type must be LastSwitch */ + break; + } + Filler->NextSwitch++; + Next = &(Filler->Switch[Filler->NextSwitch]); + } + + /* prepare the filler for the next call to this routine */ + Filler->X++; + Filler->YStart += Filler->StartDelta; + Filler->YEnd += Filler->EndDelta; + +} /* GetNextFill */ + + +/*---------------------------------------------------------------------------*/ +void +InitTableFiller (FLOAT32 EndPad, +FLOAT32 SidePad, +FLOAT32 AnglePad, PROTO Proto, TABLE_FILLER * Filler) +/* + ** Parameters: + ** EndPad, SidePad, AnglePad padding to add to proto + ** Proto proto to create a filler for + ** Filler place to put table filler + ** Globals: none + ** Operation: This routine computes a data structure (Filler) + ** which can be used to fill in a rectangle surrounding + ** the specified Proto. + ** Return: none (results are returned in Filler) + ** Exceptions: none + ** History: Thu Feb 14 09:27:05 1991, DSJ, Created. + */ +#define XS X_SHIFT +#define YS Y_SHIFT +#define AS ANGLE_SHIFT +#define NB NUM_CP_BUCKETS +{ + FLOAT32 Angle; + FLOAT32 X, Y, HalfLength; + FLOAT32 Cos, Sin; + FLOAT32 XAdjust, YAdjust; + FPOINT Start, Switch1, Switch2, End; + int S1 = 0; + int S2 = 1; + + Angle = ProtoAngle (Proto); + X = ProtoX (Proto); + Y = ProtoY (Proto); + HalfLength = ProtoLength (Proto) / 2.0; + + Filler->AngleStart = CircBucketFor (Angle - AnglePad, AS, NB); + Filler->AngleEnd = CircBucketFor (Angle + AnglePad, AS, NB); + Filler->NextSwitch = 0; + + if (fabs (Angle - 0.0) < HV_TOLERANCE || fabs (Angle - 0.5) < HV_TOLERANCE) { + /* horizontal proto - handle as special case */ + Filler->X = BucketFor (X - HalfLength - EndPad, XS, NB); + Filler->YStart = BucketFor (Y - SidePad, YS, NB * 256); + Filler->YEnd = BucketFor (Y + SidePad, YS, NB * 256); + Filler->StartDelta = 0; + Filler->EndDelta = 0; + Filler->Switch[0].Type = LastSwitch; + Filler->Switch[0].X = BucketFor (X + HalfLength + EndPad, XS, NB); + } + else if (fabs (Angle - 0.25) < HV_TOLERANCE || + fabs (Angle - 0.75) < HV_TOLERANCE) { + /* vertical proto - handle as special case */ + Filler->X = BucketFor (X - SidePad, XS, NB); + Filler->YStart = BucketFor (Y - HalfLength - EndPad, YS, NB * 256); + Filler->YEnd = BucketFor (Y + HalfLength + EndPad, YS, NB * 256); + Filler->StartDelta = 0; + Filler->EndDelta = 0; + Filler->Switch[0].Type = LastSwitch; + Filler->Switch[0].X = BucketFor (X + SidePad, XS, NB); + } + else { + /* diagonal proto */ + + if (Angle > 0.0 && Angle < 0.25 || Angle > 0.5 && Angle < 0.75) { + /* rising diagonal proto */ + Angle *= 2.0 * PI; + Cos = fabs (cos (Angle)); + Sin = fabs (sin (Angle)); + + /* compute the positions of the corners of the acceptance region */ + Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin; + Start.y = Y - (HalfLength + EndPad) * Sin + SidePad * Cos; + End.x = 2.0 * X - Start.x; + End.y = 2.0 * Y - Start.y; + Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin; + Switch1.y = Y - (HalfLength + EndPad) * Sin - SidePad * Cos; + Switch2.x = 2.0 * X - Switch1.x; + Switch2.y = 2.0 * Y - Switch1.y; + + if (Switch1.x > Switch2.x) { + S1 = 1; + S2 = 0; + } + + /* translate into bucket positions and deltas */ + Filler->X = (INT8) MapParam (Start.x, XS, NB); + Filler->StartDelta = -(INT16) ((Cos / Sin) * 256); + Filler->EndDelta = (INT16) ((Sin / Cos) * 256); + + XAdjust = BucketEnd (Filler->X, XS, NB) - Start.x; + YAdjust = XAdjust * Cos / Sin; + Filler->YStart = (INT16) MapParam (Start.y - YAdjust, YS, NB * 256); + YAdjust = XAdjust * Sin / Cos; + Filler->YEnd = (INT16) MapParam (Start.y + YAdjust, YS, NB * 256); + + Filler->Switch[S1].Type = StartSwitch; + Filler->Switch[S1].X = (INT8) MapParam (Switch1.x, XS, NB); + Filler->Switch[S1].Y = (INT8) MapParam (Switch1.y, YS, NB); + XAdjust = Switch1.x - BucketStart (Filler->Switch[S1].X, XS, NB); + YAdjust = XAdjust * Sin / Cos; + Filler->Switch[S1].YInit = + (INT16) MapParam (Switch1.y - YAdjust, YS, NB * 256); + Filler->Switch[S1].Delta = Filler->EndDelta; + + Filler->Switch[S2].Type = EndSwitch; + Filler->Switch[S2].X = (INT8) MapParam (Switch2.x, XS, NB); + Filler->Switch[S2].Y = (INT8) MapParam (Switch2.y, YS, NB); + XAdjust = Switch2.x - BucketStart (Filler->Switch[S2].X, XS, NB); + YAdjust = XAdjust * Cos / Sin; + Filler->Switch[S2].YInit = + (INT16) MapParam (Switch2.y + YAdjust, YS, NB * 256); + Filler->Switch[S2].Delta = Filler->StartDelta; + + Filler->Switch[2].Type = LastSwitch; + Filler->Switch[2].X = (INT8) MapParam (End.x, XS, NB); + } + else { + /* falling diagonal proto */ + Angle *= 2.0 * PI; + Cos = fabs (cos (Angle)); + Sin = fabs (sin (Angle)); + + /* compute the positions of the corners of the acceptance region */ + Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin; + Start.y = Y + (HalfLength + EndPad) * Sin - SidePad * Cos; + End.x = 2.0 * X - Start.x; + End.y = 2.0 * Y - Start.y; + Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin; + Switch1.y = Y + (HalfLength + EndPad) * Sin + SidePad * Cos; + Switch2.x = 2.0 * X - Switch1.x; + Switch2.y = 2.0 * Y - Switch1.y; + + if (Switch1.x > Switch2.x) { + S1 = 1; + S2 = 0; + } + + /* translate into bucket positions and deltas */ + Filler->X = (INT8) MapParam (Start.x, XS, NB); + Filler->StartDelta = -(INT16) ((Sin / Cos) * 256); + Filler->EndDelta = (INT16) ((Cos / Sin) * 256); + + XAdjust = BucketEnd (Filler->X, XS, NB) - Start.x; + YAdjust = XAdjust * Sin / Cos; + Filler->YStart = (INT16) MapParam (Start.y - YAdjust, YS, NB * 256); + YAdjust = XAdjust * Cos / Sin; + Filler->YEnd = (INT16) MapParam (Start.y + YAdjust, YS, NB * 256); + + Filler->Switch[S1].Type = EndSwitch; + Filler->Switch[S1].X = (INT8) MapParam (Switch1.x, XS, NB); + Filler->Switch[S1].Y = (INT8) MapParam (Switch1.y, YS, NB); + XAdjust = Switch1.x - BucketStart (Filler->Switch[S1].X, XS, NB); + YAdjust = XAdjust * Sin / Cos; + Filler->Switch[S1].YInit = + (INT16) MapParam (Switch1.y + YAdjust, YS, NB * 256); + Filler->Switch[S1].Delta = Filler->StartDelta; + + Filler->Switch[S2].Type = StartSwitch; + Filler->Switch[S2].X = (INT8) MapParam (Switch2.x, XS, NB); + Filler->Switch[S2].Y = (INT8) MapParam (Switch2.y, YS, NB); + XAdjust = Switch2.x - BucketStart (Filler->Switch[S2].X, XS, NB); + YAdjust = XAdjust * Cos / Sin; + Filler->Switch[S2].YInit = + (INT16) MapParam (Switch2.y - YAdjust, YS, NB * 256); + Filler->Switch[S2].Delta = Filler->EndDelta; + + Filler->Switch[2].Type = LastSwitch; + Filler->Switch[2].X = (INT8) MapParam (End.x, XS, NB); + } + } +} /* InitTableFiller */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void RenderIntFeature(void *window, INT_FEATURE Feature, C_COL Color) { +/* + ** Parameters: + ** ShapeList shape list to add feature rendering to + ** Feature feature to be rendered + ** Color color to use for feature rendering + ** Globals: none + ** Operation: This routine renders the specified feature into ShapeList. + ** Return: New shape list with rendering of Feature added. + ** Exceptions: none + ** History: Thu Mar 21 14:57:41 1991, DSJ, Created. + */ + FLOAT32 X, Y, Dx, Dy, Length; + + c_line_color_index(window, Color); + assert (Feature != NULL); + assert (Color != 0); + + X = Feature->X - DISPLAY_OFFSET; + Y = Feature->Y - DISPLAY_OFFSET; + Length = GetPicoFeatureLength () * 0.7 * INT_CHAR_NORM_RANGE; + Dx = (Length / 2.0) * cos ((Feature->Theta / 256.0) * 2.0 * PI); + Dy = (Length / 2.0) * sin ((Feature->Theta / 256.0) * 2.0 * PI); + + c_move (window, X - Dx, Y - Dy); + c_draw (window, X + Dx, Y + Dy); + c_move (window, X - Dx - Dy * DOUBLE_OFFSET, Y - Dy + Dx * DOUBLE_OFFSET); + c_draw (window, X + Dx - Dy * DOUBLE_OFFSET, Y + Dy + Dx * DOUBLE_OFFSET); +} /* RenderIntFeature */ + + +/*---------------------------------------------------------------------------*/ +void RenderIntProto(void *window, + INT_CLASS Class, + PROTO_ID ProtoId, + C_COL Color) { +/* + ** Parameters: + ** ShapeList shape list to append proto rendering onto + ** Class class that proto is contained in + ** ProtoId id of proto to be rendered + ** Color color to render proto in + ** Globals: none + ** Operation: This routine extracts the parameters of the specified + ** proto from the class description and adds a rendering of + ** the proto onto the ShapeList. + ** Return: New shape list with a rendering of one proto added. + ** Exceptions: none + ** History: Thu Mar 21 10:21:09 1991, DSJ, Created. + */ + PROTO_SET ProtoSet; + INT_PROTO Proto; + int ProtoSetIndex; + int ProtoWordIndex; + FLOAT32 Length; + int Xmin, Xmax, Ymin, Ymax; + FLOAT32 X, Y, Dx, Dy; + UINT32 ProtoMask; + int Bucket; + + assert (ProtoId >= 0); + assert (Class != NULL); + assert (ProtoId < NumIntProtosIn (Class)); + assert (Color != 0); + c_line_color_index(window, Color); + + ProtoSet = ProtoSetIn (Class, SetForProto (ProtoId)); + ProtoSetIndex = IndexForProto (ProtoId); + Proto = &(ProtoSet->Protos[ProtoSetIndex]); + Length = (LengthForProtoId (Class, ProtoId) * + GetPicoFeatureLength () * INT_CHAR_NORM_RANGE); + ProtoMask = PPrunerMaskFor (ProtoId); + ProtoWordIndex = PPrunerWordIndexFor (ProtoId); + + // find the x and y extent of the proto from the proto pruning table + Xmin = Ymin = NUM_PP_BUCKETS; + Xmax = Ymax = 0; + for (Bucket = 0; Bucket < NUM_PP_BUCKETS; Bucket++) { + if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_X][Bucket][ProtoWordIndex]) + if (Bucket < Xmin) + Xmin = Bucket; + else if (Bucket > Xmax) + Xmax = Bucket; + + if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_Y][Bucket][ProtoWordIndex]) + if (Bucket < Ymin) + Ymin = Bucket; + else if (Bucket > Ymax) + Ymax = Bucket; + } + X = (Xmin + Xmax + 1) / 2.0 * PROTO_PRUNER_SCALE - DISPLAY_OFFSET; + Y = (Ymin + Ymax + 1) / 2.0 * PROTO_PRUNER_SCALE - DISPLAY_OFFSET; + Dx = (Length / 2.0) * cos ((Proto->Angle / 256.0) * 2.0 * PI); + Dy = (Length / 2.0) * sin ((Proto->Angle / 256.0) * 2.0 * PI); + + c_move (window, X - Dx, Y - Dy); + c_draw (window, X + Dx, Y + Dy); +} /* RenderIntProto */ +#endif + +/*---------------------------------------------------------------------------*/ +int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id) { +/* + ** Parameters: + ** Param parameter value to be truncated + ** Min, Max parameter limits (inclusive) + ** Id string id of parameter for error messages + ** Globals: none + ** Operation: This routine truncates Param to lie within the range + ** of Min-Max inclusive. If a truncation is performed, and + ** Id is not null, an warning message is printed. + ** Return: Truncated parameter. + ** Exceptions: none + ** History: Fri Feb 8 11:54:28 1991, DSJ, Created. + */ + if (Param < Min) { + if (Id) + cprintf ("Warning: Param %s truncated from %f to %d!\n", + Id, Param, Min); + Param = Min; + } + else if (Param > Max) { + if (Id) + cprintf ("Warning: Param %s truncated from %f to %d!\n", + Id, Param, Max); + Param = Max; + } + return (int) floor (Param); + +} /* TruncateParam */ diff --git a/classify/intproto.h b/classify/intproto.h new file mode 100644 index 0000000000..1d8e7516e6 --- /dev/null +++ b/classify/intproto.h @@ -0,0 +1,335 @@ +/****************************************************************************** + ** Filename: intproto.h + ** Purpose: Definition of data structures for integer protos. + ** Author: Dan Johnson + ** History: Thu Feb 7 12:58:45 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef INTPROTO_H +#define INTPROTO_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "matchdefs.h" +#include "protos.h" +#include "callcpp.h" + +/* define order of params in pruners */ +#define PRUNER_X 0 +#define PRUNER_Y 1 +#define PRUNER_ANGLE 2 + +/* definition of coordinate system offsets for each table parameter */ +#define ANGLE_SHIFT (0.0) +#define X_SHIFT (0.5) +#define Y_SHIFT (0.5) + +#define MAX_PROTO_INDEX 24 +#define BITS_PER_WERD (8 * sizeof (UINT32)) +#define MAX_NUM_CONFIGS 32 +#define MAX_NUM_PROTOS 256 +#define PROTOS_PER_PROTO_SET 64 +#define MAX_NUM_PROTO_SETS (MAX_NUM_PROTOS / PROTOS_PER_PROTO_SET) +#define NUM_PP_PARAMS 3 +#define NUM_PP_BUCKETS 64 +#define NUM_CP_BUCKETS 24 +#define CLASSES_PER_CP 32 +#define NUM_BITS_PER_CLASS 2 +#define CLASSES_PER_CP_WERD (CLASSES_PER_CP / NUM_BITS_PER_CLASS) +#define PROTOS_PER_PP_WERD BITS_PER_WERD +#define BITS_PER_CP_VECTOR (CLASSES_PER_CP * NUM_BITS_PER_CLASS) +#define MAX_NUM_CLASS_PRUNERS ((MAX_NUM_CLASSES + CLASSES_PER_CP - 1) / \ + CLASSES_PER_CP) +#define WERDS_PER_CP_VECTOR (BITS_PER_CP_VECTOR / BITS_PER_WERD) +#define WERDS_PER_PP_VECTOR ((PROTOS_PER_PROTO_SET+BITS_PER_WERD-1)/ \ + BITS_PER_WERD) +#define WERDS_PER_PP (NUM_PP_PARAMS * NUM_PP_BUCKETS * \ + WERDS_PER_PP_VECTOR) +#define WERDS_PER_CP (NUM_CP_BUCKETS * NUM_CP_BUCKETS * \ + NUM_CP_BUCKETS * WERDS_PER_CP_VECTOR) +#define WERDS_PER_CONFIG_VEC ((MAX_NUM_CONFIGS + BITS_PER_WERD - 1) / \ + BITS_PER_WERD) + +typedef UINT32 CLASS_PRUNER_STRUCT +[NUM_CP_BUCKETS][NUM_CP_BUCKETS][NUM_CP_BUCKETS][WERDS_PER_CP_VECTOR]; + +typedef +UINT32 (*CLASS_PRUNER)[NUM_CP_BUCKETS][NUM_CP_BUCKETS][WERDS_PER_CP_VECTOR]; + +typedef struct +{ + INT8 A; + UINT8 B; + INT8 C; + UINT8 Angle; + UINT32 Configs[WERDS_PER_CONFIG_VEC]; +} + + +INT_PROTO_STRUCT, *INT_PROTO; + +typedef struct +{ + UINT32 ProtoPruner[NUM_PP_PARAMS][NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR]; + INT_PROTO_STRUCT Protos[PROTOS_PER_PROTO_SET]; +} + + +PROTO_SET_STRUCT, *PROTO_SET; + +typedef UINT32 CONFIG_PRUNER[NUM_PP_PARAMS][NUM_PP_BUCKETS][4]; + +typedef struct +{ + UINT16 NumProtos; + UINT8 NumProtoSets; + UINT8 NumConfigs; + PROTO_SET ProtoSets[MAX_NUM_PROTO_SETS]; + UINT8 *ProtoLengths; + UINT16 ConfigLengths[MAX_NUM_CONFIGS]; +} + + +INT_CLASS_STRUCT, *INT_CLASS; + +typedef struct +{ + int NumClasses; + int NumClassPruners; + CLASS_TO_INDEX IndexFor; /*int16[256] */ + INDEX_TO_CLASS ClassIdFor; /*unit8[100 */ + INT_CLASS Class[MAX_NUM_CLASSES]; + CLASS_PRUNER ClassPruner[MAX_NUM_CLASS_PRUNERS]; +} + + +INT_TEMPLATES_STRUCT, *INT_TEMPLATES; + +/* definitions of integer features*/ +#define MAX_NUM_INT_FEATURES 512 +#define INT_CHAR_NORM_RANGE 256 + +typedef struct +{ + UINT8 X; + UINT8 Y; + UINT8 Theta; + INT8 CP_misses; +} + + +INT_FEATURE_STRUCT; +typedef INT_FEATURE_STRUCT *INT_FEATURE; + +typedef INT_FEATURE_STRUCT INT_FEATURE_ARRAY[MAX_NUM_INT_FEATURES]; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* PROTO_SET access macros*/ +#define ProtoPrunerFor(S) (S->ProtoPruner) + +/* INT_CLASS access macros*/ +#define NumIntProtosIn(C) ((C)->NumProtos) +#define NumProtoSetsIn(C) ((C)->NumProtoSets) +#define MaxNumIntProtosIn(C) (NumProtoSetsIn (C) * PROTOS_PER_PROTO_SET) +#define NumIntConfigsIn(C) ((C)->NumConfigs) +#define ProtoSetIn(C,I) ((C)->ProtoSets[I]) +#define SetForProto(P) (P / PROTOS_PER_PROTO_SET) +#define IndexForProto(P) (P % PROTOS_PER_PROTO_SET) +//#define IllegalProto(C,P) (P >= MaxNumIntProtosIn (C)) +#define ProtoForProtoId(C,P) (&((ProtoSetIn (C, SetForProto (P)))-> \ + Protos [IndexForProto (P)])) +#define LengthForProtoId(C,P) ((C)->ProtoLengths[P]) +#define LengthForConfigId(C,c) ((C)->ConfigLengths[c]) +#define PPrunerWordIndexFor(I) (((I) % PROTOS_PER_PROTO_SET) / \ + PROTOS_PER_PP_WERD) +#define PPrunerBitIndexFor(I) ((I) % PROTOS_PER_PP_WERD) +#define PPrunerMaskFor(I) (1 << PPrunerBitIndexFor (I)) + +/* INT_TEMPLATE access macros*/ +#define NumClassesIn(T) ((T)->NumClasses) +#define NumClassPrunersIn(T) ((T)->NumClassPruners) +#define MaxNumClassesIn(T) (NumClassPrunersIn (T) * CLASSES_PER_CP) +#define ClassIdForIndex(T,I) ((T)->ClassIdFor[I]) +#define IndexForClassId(T,C) ((T)->IndexFor[C]) +#define LegalClassId(C) ((C) > 0 && (C) < MAX_CLASS_ID) +#define UnusedClassIdIn(T,C) (IndexForClassId (T,C) == ILLEGAL_CLASS) +#define ClassForIndex(T,I) ((T)->Class[I]) +#define ClassForClassId(T,C) (ClassForIndex (T, IndexForClassId (T, C))) +#define ClassPrunersFor(T) ((T)->ClassPruner) +#define CPrunerIdFor(I) ((I) / CLASSES_PER_CP) +#define CPrunerFor(T,I) ((T)->ClassPruner [CPrunerIdFor (I)]) +#define CPrunerWordIndexFor(I) (((I) % CLASSES_PER_CP) / CLASSES_PER_CP_WERD) +#define CPrunerBitIndexFor(I) (((I) % CLASSES_PER_CP) % CLASSES_PER_CP_WERD) +#define CPrunerMaskFor(L,I) (((L)+1) << CPrunerBitIndexFor (I) * NUM_BITS_PER_CLASS) + +/* DEBUG macros*/ +#define PRINT_MATCH_SUMMARY 0x001 +#define DISPLAY_FEATURE_MATCHES 0x002 +#define DISPLAY_PROTO_MATCHES 0x004 +#define PRINT_FEATURE_MATCHES 0x008 +#define PRINT_PROTO_MATCHES 0x010 +#define CLIP_MATCH_EVIDENCE 0x020 + +#define MatchDebuggingOn(D) (D) +#define PrintMatchSummaryOn(D) ((D) & PRINT_MATCH_SUMMARY) +#define DisplayFeatureMatchesOn(D) ((D) & DISPLAY_FEATURE_MATCHES) +#define DisplayProtoMatchesOn(D) ((D) & DISPLAY_PROTO_MATCHES) +#define PrintFeatureMatchesOn(D) ((D) & PRINT_FEATURE_MATCHES) +#define PrintProtoMatchesOn(D) ((D) & PRINT_PROTO_MATCHES) +#define ClipMatchEvidenceOn(D) ((D) & CLIP_MATCH_EVIDENCE) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +int AddIntClass(INT_TEMPLATES Templates, CLASS_ID ClassId, INT_CLASS Class); + +int AddIntConfig(INT_CLASS Class); + +int AddIntProto(INT_CLASS Class); + +void AddProtoToClassPruner(PROTO Proto, + CLASS_ID ClassId, + INT_TEMPLATES Templates); + +void AddProtoToProtoPruner(PROTO Proto, int ProtoId, INT_CLASS Class); + +int BucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets); + +int CircBucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets); + +void UpdateMatchDisplay(); + +void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class); + +void ConvertProto(PROTO Proto, int ProtoId, INT_CLASS Class); + +INT_TEMPLATES CreateIntTemplates(CLASSES FloatProtos); + +void DisplayIntFeature(INT_FEATURE Feature, FLOAT32 Evidence); + +void DisplayIntProto(INT_CLASS Class, PROTO_ID ProtoId, FLOAT32 Evidence); + +void InitIntProtoVars(); + +INT_CLASS NewIntClass(int MaxNumProtos, int MaxNumConfigs); + +void free_int_class(INT_CLASS int_class); + +INT_TEMPLATES NewIntTemplates(); + +void free_int_templates(INT_TEMPLATES templates); + +INT_TEMPLATES ReadIntTemplates(FILE *File, BOOL8 swap); + +void ShowMatchDisplay(); + +CLASS_ID GetClassToDebug(const char *Prompt); + +void WriteIntTemplates(FILE *File, INT_TEMPLATES Templates); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* intproto.c +int AddIntClass + _ARGS((INT_TEMPLATES Templates, + CLASS_ID ClassId, + INT_CLASS Class)); + +int AddIntConfig + _ARGS((INT_CLASS Class)); + +int AddIntProto + _ARGS((INT_CLASS Class)); + +void AddProtoToClassPruner + _ARGS((PROTO Proto, + CLASS_ID ClassId, + INT_TEMPLATES Templates)); + +void AddProtoToProtoPruner + _ARGS((PROTO Proto, + int ProtoId, + INT_CLASS Class)); + +int BucketFor + _ARGS((FLOAT32 Param, + FLOAT32 Offset, + int NumBuckets)); + +int CircBucketFor + _ARGS((FLOAT32 Param, + FLOAT32 Offset, + int NumBuckets)); + +void UpdateMatchDisplay + _ARGS((void)); + +void ConvertConfig + _ARGS((BIT_VECTOR Config, + int ConfigId, + INT_CLASS Class)); + +void ConvertProto + _ARGS((PROTO Proto, + int ProtoId, + INT_CLASS Class)); + +INT_TEMPLATES CreateIntTemplates + _ARGS((CLASSES FloatProtos)); + +void DisplayIntFeature + _ARGS((INT_FEATURE Feature, + FLOAT32 Evidence)); + +void DisplayIntProto + _ARGS((INT_CLASS Class, + PROTO_ID ProtoId, + FLOAT32 Evidence)); + +void InitIntProtoVars + _ARGS((void)); + +INT_CLASS NewIntClass + _ARGS((int MaxNumProtos, + int MaxNumConfigs)); + +INT_TEMPLATES NewIntTemplates + _ARGS((void)); + +INT_TEMPLATES ReadIntTemplates + _ARGS((FILE *File)); + +void ShowMatchDisplay + _ARGS((void)); + +void WriteIntTemplates + _ARGS((FILE *File, + INT_TEMPLATES Templates)); + +CLASS_ID GetClassToDebug + _ARGS((char *Prompt)); + +C_COL GetMatchColorFor + _ARGS((FLOAT32 Evidence)); + +#undef _ARGS +*/ +#endif diff --git a/classify/kdtree.cpp b/classify/kdtree.cpp new file mode 100644 index 0000000000..609fe81bde --- /dev/null +++ b/classify/kdtree.cpp @@ -0,0 +1,872 @@ +/****************************************************************************** + ** Filename: kdtree.c + ** Purpose: Routines for managing K-D search trees + ** Author: Dan Johnson + ** History: 3/10/89, DSJ, Created. + ** 5/23/89, DSJ, Added circular feature capability. + ** 7/13/89, DSJ, Made tree nodes invisible to outside. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "kdtree.h" +#include "const.h" +#include "emalloc.h" +#include "freelist.h" +#include +#include +#include + +#define Magnitude(X) ((X) < 0 ? -(X) : (X)) +#define MIN(A,B) ((A) < (B) ? (A) : (B)) +#define NodeFound(N,K,D) (( (N)->Key == (K) ) && ( (N)->Data == (D) )) + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#define MINSEARCH -MAX_FLOAT32 +#define MAXSEARCH MAX_FLOAT32 + +static int NumberOfNeighbors; +static INT16 N; /* number of dimensions in the kd tree */ + +static FLOAT32 *QueryPoint; +static int MaxNeighbors; +static FLOAT32 Radius; +static int Furthest; +static char **Neighbor; +static FLOAT32 *Distance; + +static int MaxDimension = 0; +static FLOAT32 *SBMin; +static FLOAT32 *SBMax; +static FLOAT32 *LBMin; +static FLOAT32 *LBMax; + +static PARAM_DESC *KeyDesc; + +static jmp_buf QuickExit; + +static void_proc WalkAction; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +KDTREE * +MakeKDTree (INT16 KeySize, PARAM_DESC KeyDesc[]) { +/* + ** Parameters: + ** KeySize # of dimensions in the K-D tree + ** KeyDesc array of params to describe key dimensions + ** Globals: + ** MaxDimension largest # of dimensions in any K-D tree + ** SBMin small search region box + ** SBMax + ** LBMin large search region box + ** LBMax + ** Key description of key dimensions + ** Operation: + ** This routine allocates and returns a new K-D tree data + ** structure. It also reallocates the small and large + ** search region boxes if they are not large enough to + ** accomodate the size of the new K-D tree. KeyDesc is + ** an array of key descriptors that indicate which dimensions + ** are circular and, if they are circular, what the range is. + ** Return: + ** Pointer to new K-D tree + ** Exceptions: + ** None + ** History: + ** 3/13/89, DSJ, Created. + */ + int i; + void *NewMemory; + KDTREE *KDTree; + + if (KeySize > MaxDimension) { + NewMemory = Emalloc (KeySize * 4 * sizeof (FLOAT32)); + if (MaxDimension > 0) { + memfree ((char *) SBMin); + memfree ((char *) SBMax); + memfree ((char *) LBMin); + memfree ((char *) LBMax); + } + SBMin = (FLOAT32 *) NewMemory; + SBMax = SBMin + KeySize; + LBMin = SBMax + KeySize; + LBMax = LBMin + KeySize; + } + + KDTree = + (KDTREE *) Emalloc (sizeof (KDTREE) + + (KeySize - 1) * sizeof (PARAM_DESC)); + for (i = 0; i < KeySize; i++) { + KDTree->KeyDesc[i].NonEssential = KeyDesc[i].NonEssential; + KDTree->KeyDesc[i].Circular = KeyDesc[i].Circular; + if (KeyDesc[i].Circular) { + KDTree->KeyDesc[i].Min = KeyDesc[i].Min; + KDTree->KeyDesc[i].Max = KeyDesc[i].Max; + KDTree->KeyDesc[i].Range = KeyDesc[i].Max - KeyDesc[i].Min; + KDTree->KeyDesc[i].HalfRange = KDTree->KeyDesc[i].Range / 2; + KDTree->KeyDesc[i].MidRange = (KeyDesc[i].Max + KeyDesc[i].Min) / 2; + } + else { + KDTree->KeyDesc[i].Min = MINSEARCH; + KDTree->KeyDesc[i].Max = MAXSEARCH; + } + } + KDTree->KeySize = KeySize; + KDTree->Root.Left = NULL; + KDTree->Root.Right = NULL; + return (KDTree); +} /* MakeKDTree */ + + +/*---------------------------------------------------------------------------*/ +void KDStore(KDTREE *Tree, FLOAT32 *Key, void *Data) { +/* + ** Parameters: + ** Tree K-D tree in which data is to be stored + ** Key ptr to key by which data can be retrieved + ** Data ptr to data to be stored in the tree + ** Globals: + ** N dimension of the K-D tree + ** KeyDesc descriptions of tree dimensions + ** StoreCount debug variables for performance tests + ** StoreUniqueCount + ** StoreProbeCount + ** Operation: + ** This routine stores Data in the K-D tree specified by Tree + ** using Key as an access key. + ** Return: none + ** Exceptions: none + ** History: 3/10/89, DSJ, Created. + ** 7/13/89, DSJ, Changed return to void. + */ + int Level; + KDNODE *Node; + KDNODE **PtrToNode; + + N = Tree->KeySize; + KeyDesc = &(Tree->KeyDesc[0]); + PtrToNode = &(Tree->Root.Left); + Node = *PtrToNode; + Level = 0; + while (Node != NULL) { + if (Key[Level] < Node->BranchPoint) { + PtrToNode = &(Node->Left); + if (Key[Level] > Node->LeftBranch) + Node->LeftBranch = Key[Level]; + } + else { + PtrToNode = &(Node->Right); + if (Key[Level] < Node->RightBranch) + Node->RightBranch = Key[Level]; + } + Level++; + if (Level >= N) + Level = 0; + Node = *PtrToNode; + } + + *PtrToNode = MakeKDNode (Key, (char *) Data, Level); +} /* KDStore */ + + +/*---------------------------------------------------------------------------*/ +void +KDDelete (KDTREE * Tree, FLOAT32 Key[], void *Data) { +/* + ** Parameters: + ** Tree K-D tree to delete node from + ** Key key of node to be deleted + ** Data data contents of node to be deleted + ** Globals: + ** N dimension of the K-D tree + ** KeyDesc description of each dimension + ** DeleteCount debug variables for performance tests + ** DeleteProbeCount + ** Operation: + ** This routine deletes a node from Tree. The node to be + ** deleted is specified by the Key for the node and the Data + ** contents of the node. These two pointers must be identical + ** to the pointers that were used for the node when it was + ** originally stored in the tree. A node will be deleted from + ** the tree only if its key and data pointers are identical + ** to Key and Data respectively. The empty space left in the tree + ** is filled by pulling a leaf up from the bottom of one of + ** the subtrees of the node being deleted. The leaf node will + ** be pulled from left subtrees whenever possible (this was + ** an arbitrary decision). No attempt is made to pull the leaf + ** from the deepest subtree (to minimize length). The branch + ** point for the replacement node is changed to be the same as + ** the branch point of the deleted node. This keeps us from + ** having to rearrange the tree every time we delete a node. + ** Also, the LeftBranch and RightBranch numbers of the + ** replacement node are set to be the same as the deleted node. + ** The makes the delete easier and more efficient, but it may + ** make searches in the tree less efficient after many nodes are + ** deleted. If the node specified by Key and Data does not + ** exist in the tree, then nothing is done. + ** Return: none + ** None + ** Exceptions: none + ** None + ** History: 3/13/89, DSJ, Created. + ** 7/13/89, DSJ, Specify node indirectly by key and data. + */ + int Level; + KDNODE *Current; + KDNODE *Father; + KDNODE *Replacement; + KDNODE *FatherReplacement; + + /* initialize search at root of tree */ + N = Tree->KeySize; + KeyDesc = &(Tree->KeyDesc[0]); + Father = &(Tree->Root); + Current = Father->Left; + Level = 0; + + /* search tree for node to be deleted */ + while ((Current != NULL) && (!NodeFound (Current, Key, Data))) { + Father = Current; + if (Key[Level] < Current->BranchPoint) + Current = Current->Left; + else + Current = Current->Right; + + Level++; + if (Level >= N) + Level = 0; + } + + if (Current != NULL) { /* if node to be deleted was found */ + Replacement = Current; + FatherReplacement = Father; + + /* search for replacement node (a leaf under node to be deleted */ + while (TRUE) { + if (Replacement->Left != NULL) { + FatherReplacement = Replacement; + Replacement = Replacement->Left; + } + else if (Replacement->Right != NULL) { + FatherReplacement = Replacement; + Replacement = Replacement->Right; + } + else + break; + + Level++; + if (Level >= N) + Level = 0; + } + + /* compute level of replacement node's father */ + Level--; + if (Level < 0) + Level = N - 1; + + /* disconnect replacement node from it's father */ + if (FatherReplacement->Left == Replacement) { + FatherReplacement->Left = NULL; + FatherReplacement->LeftBranch = KeyDesc[Level].Min; + } + else { + FatherReplacement->Right = NULL; + FatherReplacement->RightBranch = KeyDesc[Level].Max; + } + + /* replace deleted node with replacement (unless they are the same) */ + if (Replacement != Current) { + Replacement->BranchPoint = Current->BranchPoint; + Replacement->LeftBranch = Current->LeftBranch; + Replacement->RightBranch = Current->RightBranch; + Replacement->Left = Current->Left; + Replacement->Right = Current->Right; + + if (Father->Left == Current) + Father->Left = Replacement; + else + Father->Right = Replacement; + } + FreeKDNode(Current); + } +} /* KDDelete */ + + +/*---------------------------------------------------------------------------*/ +int +KDNearestNeighborSearch (KDTREE * Tree, +FLOAT32 Query[], +int QuerySize, +FLOAT32 MaxDistance, +void *NBuffer, FLOAT32 DBuffer[]) { +/* + ** Parameters: + ** Tree ptr to K-D tree to be searched + ** Query ptr to query key (point in D-space) + ** QuerySize number of nearest neighbors to be found + ** MaxDistance all neighbors must be within this distance + ** NBuffer ptr to QuerySize buffer to hold nearest neighbors + ** DBuffer ptr to QuerySize buffer to hold distances + ** from nearest neighbor to query point + ** Globals: + ** NumberOfNeighbors # of neighbors found so far + ** N # of features in each key + ** KeyDesc description of tree dimensions + ** QueryPoint point in D-space to find neighbors of + ** MaxNeighbors maximum # of neighbors to find + ** Radius current distance of furthest neighbor + ** Furthest index of furthest neighbor + ** Neighbor buffer of current neighbors + ** Distance buffer of neighbor distances + ** SBMin lower extent of small search region + ** SBMax upper extent of small search region + ** LBMin lower extent of large search region + ** LBMax upper extent of large search region + ** QuickExit quick exit from recursive search + ** Operation: + ** This routine searches the K-D tree specified by Tree and + ** finds the QuerySize nearest neighbors of Query. All neighbors + ** must be within MaxDistance of Query. The data contents of + ** the nearest neighbors + ** are placed in NBuffer and their distances from Query are + ** placed in DBuffer. + ** Return: Number of nearest neighbors actually found + ** Exceptions: none + ** History: + ** 3/10/89, DSJ, Created. + ** 7/13/89, DSJ, Return contents of node instead of node itself. + */ + int i; + + NumberOfNeighbors = 0; + N = Tree->KeySize; + KeyDesc = &(Tree->KeyDesc[0]); + QueryPoint = Query; + MaxNeighbors = QuerySize; + Radius = MaxDistance; + Furthest = 0; + Neighbor = (char **) NBuffer; + Distance = DBuffer; + + for (i = 0; i < N; i++) { + SBMin[i] = KeyDesc[i].Min; + SBMax[i] = KeyDesc[i].Max; + LBMin[i] = KeyDesc[i].Min; + LBMax[i] = KeyDesc[i].Max; + } + + if (Tree->Root.Left != NULL) { + if (setjmp (QuickExit) == 0) + Search (0, Tree->Root.Left); + } + return (NumberOfNeighbors); +} /* KDNearestNeighborSearch */ + + +/*---------------------------------------------------------------------------*/ +void KDWalk(KDTREE *Tree, void_proc Action) { +/* + ** Parameters: + ** Tree ptr to K-D tree to be walked + ** Action ptr to function to be executed at each node + ** Globals: + ** WalkAction action to be performed at every node + ** Operation: + ** This routine stores the desired action in a global + ** variable and starts a recursive walk of Tree. The walk + ** is started at the root node. + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 3/13/89, DSJ, Created. + */ + WalkAction = Action; + if (Tree->Root.Left != NULL) + Walk (Tree->Root.Left, 0); +} /* KDWalk */ + + +/*---------------------------------------------------------------------------*/ +void FreeKDTree(KDTREE *Tree) { +/* + ** Parameters: + ** Tree tree data structure to be released + ** Globals: none + ** Operation: + ** This routine frees all memory which is allocated to the + ** specified KD-tree. This includes the data structure for + ** the kd-tree itself plus the data structures for each node + ** in the tree. It does not include the Key and Data items + ** which are pointed to by the nodes. This memory is left + ** untouched. + ** Return: none + ** Exceptions: none + ** History: + ** 5/26/89, DSJ, Created. + */ + FreeSubTree (Tree->Root.Left); + memfree(Tree); +} /* FreeKDTree */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int +Equal (FLOAT32 Key1[], FLOAT32 Key2[]) { +/* + ** Parameters: + ** Key1,Key2 search keys to be compared for equality + ** Globals: + ** N number of parameters per key + ** Operation: + ** This routine returns TRUE if Key1 = Key2. + ** Return: + ** TRUE if Key1 = Key2, else FALSE. + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + int i; + + for (i = N; i > 0; i--, Key1++, Key2++) + if (*Key1 != *Key2) + return (FALSE); + return (TRUE); +} /* Equal */ + + +/*---------------------------------------------------------------------------*/ +KDNODE * +MakeKDNode (FLOAT32 Key[], char *Data, int Index) { +/* + ** Parameters: + ** Key Access key for new node in KD tree + ** Data ptr to data to be stored in new node + ** Index index of Key to branch on + ** Globals: + ** KeyDesc descriptions of key dimensions + ** Operation: + ** This routine allocates memory for a new K-D tree node + ** and places the specified Key and Data into it. The + ** left and right subtree pointers for the node are + ** initialized to empty subtrees. + ** Return: + ** pointer to new K-D tree node + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + KDNODE *NewNode; + + NewNode = (KDNODE *) Emalloc (sizeof (KDNODE)); + + NewNode->Key = Key; + NewNode->Data = Data; + NewNode->BranchPoint = Key[Index]; + NewNode->LeftBranch = KeyDesc[Index].Min; + NewNode->RightBranch = KeyDesc[Index].Max; + NewNode->Left = NULL; + NewNode->Right = NULL; + + return (NewNode); +} /* MakeKDNode */ + + +/*---------------------------------------------------------------------------*/ +void FreeKDNode(KDNODE *Node) { +/* + ** Parameters: + ** Node ptr to node data structure to be freed + ** Globals: + ** None + ** Operation: + ** This routine frees up the memory allocated to Node. + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 3/13/89, DSJ, Created. + */ + memfree ((char *) Node); +} /* FreeKDNode */ + + +/*---------------------------------------------------------------------------*/ +void Search(int Level, KDNODE *SubTree) { +/* + ** Parameters: + ** Level level in tree of sub-tree to be searched + ** SubTree sub-tree to be searched + ** Globals: + ** NumberOfNeighbors # of neighbors found so far + ** N # of features in each key + ** KeyDesc description of key dimensions + ** QueryPoint point in D-space to find neighbors of + ** MaxNeighbors maximum # of neighbors to find + ** Radius current distance of furthest neighbor + ** Furthest index of furthest neighbor + ** Neighbor buffer of current neighbors + ** Distance buffer of neighbor distances + ** SBMin lower extent of small search region + ** SBMax upper extent of small search region + ** LBMin lower extent of large search region + ** LBMax upper extent of large search region + ** QuickExit quick exit from recursive search + ** Operation: + ** This routine searches SubTree for those entries which are + ** possibly among the MaxNeighbors nearest neighbors of the + ** QueryPoint and places their data in the Neighbor buffer and + ** their distances from QueryPoint in the Distance buffer. + ** Return: none + ** Exceptions: none + ** History: + ** 3/11/89, DSJ, Created. + ** 7/13/89, DSJ, Save node contents, not node, in neighbor buffer + */ + FLOAT32 d; + FLOAT32 OldSBoxEdge; + FLOAT32 OldLBoxEdge; + + if (Level >= N) + Level = 0; + + d = ComputeDistance (N, KeyDesc, QueryPoint, SubTree->Key); + if (d < Radius) { + if (NumberOfNeighbors < MaxNeighbors) { + Neighbor[NumberOfNeighbors] = SubTree->Data; + Distance[NumberOfNeighbors] = d; + NumberOfNeighbors++; + if (NumberOfNeighbors == MaxNeighbors) + FindMaxDistance(); + } + else { + Neighbor[Furthest] = SubTree->Data; + Distance[Furthest] = d; + FindMaxDistance(); + } + } + if (QueryPoint[Level] < SubTree->BranchPoint) { + OldSBoxEdge = SBMax[Level]; + SBMax[Level] = SubTree->LeftBranch; + OldLBoxEdge = LBMax[Level]; + LBMax[Level] = SubTree->RightBranch; + if (SubTree->Left != NULL) + Search (Level + 1, SubTree->Left); + SBMax[Level] = OldSBoxEdge; + LBMax[Level] = OldLBoxEdge; + OldSBoxEdge = SBMin[Level]; + SBMin[Level] = SubTree->RightBranch; + OldLBoxEdge = LBMin[Level]; + LBMin[Level] = SubTree->LeftBranch; + if ((SubTree->Right != NULL) && QueryIntersectsSearch ()) + Search (Level + 1, SubTree->Right); + SBMin[Level] = OldSBoxEdge; + LBMin[Level] = OldLBoxEdge; + } + else { + OldSBoxEdge = SBMin[Level]; + SBMin[Level] = SubTree->RightBranch; + OldLBoxEdge = LBMin[Level]; + LBMin[Level] = SubTree->LeftBranch; + if (SubTree->Right != NULL) + Search (Level + 1, SubTree->Right); + SBMin[Level] = OldSBoxEdge; + LBMin[Level] = OldLBoxEdge; + OldSBoxEdge = SBMax[Level]; + SBMax[Level] = SubTree->LeftBranch; + OldLBoxEdge = LBMax[Level]; + LBMax[Level] = SubTree->RightBranch; + if ((SubTree->Left != NULL) && QueryIntersectsSearch ()) + Search (Level + 1, SubTree->Left); + SBMax[Level] = OldSBoxEdge; + LBMax[Level] = OldLBoxEdge; + } + if (QueryInSearch ()) + longjmp (QuickExit, 1); +} /* Search */ + + +/*---------------------------------------------------------------------------*/ +FLOAT32 +ComputeDistance (register int N, +register PARAM_DESC Dim[], +register FLOAT32 p1[], register FLOAT32 p2[]) { +/* + ** Parameters: + ** N number of dimensions in K-D space + ** Dim descriptions of each dimension + ** p1,p2 two different points in K-D space + ** Globals: + ** None + ** Operation: + ** This routine computes the euclidian distance + ** between p1 and p2 in K-D space (an N dimensional space). + ** Return: + ** Distance between p1 and p2. + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + register FLOAT32 TotalDistance; + register FLOAT32 DimensionDistance; + FLOAT32 WrapDistance; + + TotalDistance = 0; + for (; N > 0; N--, p1++, p2++, Dim++) { + if (Dim->NonEssential) + continue; + + DimensionDistance = *p1 - *p2; + + /* if this dimension is circular - check wraparound distance */ + if (Dim->Circular) { + DimensionDistance = Magnitude (DimensionDistance); + WrapDistance = Dim->Max - Dim->Min - DimensionDistance; + DimensionDistance = MIN (DimensionDistance, WrapDistance); + } + + TotalDistance += DimensionDistance * DimensionDistance; + } + return ((FLOAT32) sqrt ((FLOAT64) TotalDistance)); +} /* ComputeDistance */ + + +/*---------------------------------------------------------------------------*/ +void FindMaxDistance() { +/* + ** Parameters: + ** None + ** Globals: + ** MaxNeighbors maximum # of neighbors to find + ** Radius current distance of furthest neighbor + ** Furthest index of furthest neighbor + ** Distance buffer of neighbor distances + ** Operation: + ** This routine searches the Distance buffer for the maximum + ** distance, places this distance in Radius, and places the + ** index of this distance in Furthest. + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + int i; + + Radius = Distance[Furthest]; + for (i = 0; i < MaxNeighbors; i++) { + if (Distance[i] > Radius) { + Radius = Distance[i]; + Furthest = i; + } + } +} /* FindMaxDistance */ + + +/*---------------------------------------------------------------------------*/ +int QueryIntersectsSearch() { +/* + ** Parameters: + ** None + ** Globals: + ** N # of features in each key + ** KeyDesc descriptions of each dimension + ** QueryPoint point in D-space to find neighbors of + ** Radius current distance of furthest neighbor + ** SBMin lower extent of small search region + ** SBMax upper extent of small search region + ** Operation: + ** This routine returns TRUE if the query region intersects + ** the current smallest search region. The query region is + ** the circle of radius Radius centered at QueryPoint. + ** The smallest search region is the box (in N dimensions) + ** whose edges in each dimension are specified by SBMin and SBMax. + ** In the case of circular dimensions, we must also check the + ** point which is one wrap-distance away from the query to + ** see if it would intersect the search region. + ** Return: + ** TRUE if query region intersects search region, else FALSE + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + register int i; + register FLOAT32 *Query; + register FLOAT32 *Lower; + register FLOAT32 *Upper; + register FLOAT64 TotalDistance; + register FLOAT32 DimensionDistance; + register FLOAT64 RadiusSquared; + register PARAM_DESC *Dim; + register FLOAT32 WrapDistance; + + RadiusSquared = Radius * Radius; + Query = QueryPoint; + Lower = SBMin; + Upper = SBMax; + TotalDistance = 0.0; + Dim = KeyDesc; + for (i = N; i > 0; i--, Dim++, Query++, Lower++, Upper++) { + if (Dim->NonEssential) + continue; + + if (*Query < *Lower) + DimensionDistance = *Lower - *Query; + else if (*Query > *Upper) + DimensionDistance = *Query - *Upper; + else + DimensionDistance = 0; + + /* if this dimension is circular - check wraparound distance */ + if (Dim->Circular) { + if (*Query < *Lower) + WrapDistance = *Query + Dim->Max - Dim->Min - *Upper; + else if (*Query > *Upper) + WrapDistance = *Lower - (*Query - (Dim->Max - Dim->Min)); + else + WrapDistance = MAX_FLOAT32; + + DimensionDistance = MIN (DimensionDistance, WrapDistance); + } + + TotalDistance += DimensionDistance * DimensionDistance; + if (TotalDistance >= RadiusSquared) + return (FALSE); + } + return (TRUE); +} /* QueryIntersectsSearch */ + + +/*---------------------------------------------------------------------------*/ +int QueryInSearch() { +/* + ** Parameters: + ** None + ** Globals: + ** N # of features in each key + ** KeyDesc descriptions of each dimension + ** QueryPoint point in D-space to find neighbors of + ** Radius current distance of furthest neighbor + ** LBMin lower extent of large search region + ** LBMax upper extent of large search region + ** Operation: + ** This routine returns TRUE if the current query region is + ** totally contained in the current largest search region. + ** The query region is the circle of + ** radius Radius centered at QueryPoint. The search region is + ** the box (in N dimensions) whose edges in each + ** dimension are specified by LBMin and LBMax. + ** Return: + ** TRUE if query region is inside search region, else FALSE + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + register int i; + register FLOAT32 *Query; + register FLOAT32 *Lower; + register FLOAT32 *Upper; + register PARAM_DESC *Dim; + + Query = QueryPoint; + Lower = LBMin; + Upper = LBMax; + Dim = KeyDesc; + + for (i = N - 1; i >= 0; i--, Dim++, Query++, Lower++, Upper++) { + if (Dim->NonEssential) + continue; + + if ((*Query < *Lower + Radius) || (*Query > *Upper - Radius)) + return (FALSE); + } + return (TRUE); +} /* QueryInSearch */ + + +/*---------------------------------------------------------------------------*/ +void Walk(KDNODE *SubTree, INT32 Level) { +/* + ** Parameters: + ** SubTree ptr to root of subtree to be walked + ** Level current level in the tree for this node + ** Globals: + ** WalkAction action to be performed at every node + ** Operation: + ** This routine walks thru the specified SubTree and invokes + ** WalkAction at each node. WalkAction is invoked with three + ** arguments as follows: + ** WalkAction( NodeData, Order, Level ) + ** Data is the data contents of the node being visited, + ** Order is either preorder, + ** postorder, endorder, or leaf depending on whether this is + ** the 1st, 2nd, or 3rd time a node has been visited, or + ** whether the node is a leaf. Level is the level of the node in + ** the tree with the root being level 0. + ** Return: none + ** Exceptions: none + ** History: + ** 3/13/89, DSJ, Created. + ** 7/13/89, DSJ, Pass node contents, not node, to WalkAction(). + */ + if ((SubTree->Left == NULL) && (SubTree->Right == NULL)) + (*WalkAction) (SubTree->Data, leaf, Level); + else { + (*WalkAction) (SubTree->Data, preorder, Level); + if (SubTree->Left != NULL) + Walk (SubTree->Left, Level + 1); + (*WalkAction) (SubTree->Data, postorder, Level); + if (SubTree->Right != NULL) + Walk (SubTree->Right, Level + 1); + (*WalkAction) (SubTree->Data, endorder, Level); + } +} /* Walk */ + + +/*---------------------------------------------------------------------------*/ +void FreeSubTree(KDNODE *SubTree) { +/* + ** Parameters: + ** SubTree ptr to root node of sub-tree to be freed + ** Globals: none + ** Operation: + ** This routine recursively frees the memory allocated to + ** to the specified subtree. + ** Return: none + ** Exceptions: none + ** History: 7/13/89, DSJ, Created. + */ + if (SubTree != NULL) { + FreeSubTree (SubTree->Left); + FreeSubTree (SubTree->Right); + memfree(SubTree); + } +} /* FreeSubTree */ diff --git a/classify/kdtree.h b/classify/kdtree.h new file mode 100644 index 0000000000..9a4c4ef8a4 --- /dev/null +++ b/classify/kdtree.h @@ -0,0 +1,118 @@ +/****************************************************************************** + ** Filename: kdtree.h + ** Purpose: Definition of K-D tree access routines. + ** Author: Dan Johnson + ** History: 3/11/89, DSJ, Created. + ** 5/23/89, DSJ, Added circular feature capability. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef KDTREE_H +#define KDTREE_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "general.h" +#include "cutil.h" +#include "ocrfeatures.h" + +/* +NOTE: All circular parameters of all keys must be in the range + +Min <= Param < Max + +where Min and Max are specified in the KeyDesc parameter passed to +MakeKDTree. All KD routines assume that this is true and will not operate +correctly if circular parameters outside the specified range are used. +*/ + +typedef struct kdnode +{ + FLOAT32 *Key; /* search key */ + char *Data; /* data that corresponds to key */ + FLOAT32 BranchPoint; /* needed to make deletes work efficiently */ + FLOAT32 LeftBranch; /* used to optimize search pruning */ + FLOAT32 RightBranch; /* used to optimize search pruning */ + struct kdnode *Left; /* ptrs for KD tree structure */ + struct kdnode *Right; +} + + +KDNODE; + +typedef struct +{ + INT16 KeySize; /* number of dimensions in the tree */ + KDNODE Root; /* Root.Left points to actual root node */ + PARAM_DESC KeyDesc[1]; /* description of each dimension */ +} + + +KDTREE; + +typedef enum { /* used for walking thru KD trees */ + preorder, postorder, endorder, leaf +} + + +VISIT; + +/*---------------------------------------------------------------------------- + Macros +-----------------------------------------------------------------------------*/ +#define RootOf(T) ((T)->Root.Left->Data) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +KDTREE *MakeKDTree (INT16 KeySize, PARAM_DESC KeyDesc[]); + +void KDStore(KDTREE *Tree, FLOAT32 *Key, void *Data); + +void KDDelete (KDTREE * Tree, FLOAT32 Key[], void *Data); + +int KDNearestNeighborSearch (KDTREE * Tree, +FLOAT32 Query[], +int QuerySize, +FLOAT32 MaxDistance, +void *NBuffer, FLOAT32 DBuffer[]); + +void KDWalk(KDTREE *Tree, void_proc Action); + +void FreeKDTree(KDTREE *Tree); + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +int Equal (FLOAT32 Key1[], FLOAT32 Key2[]); + +KDNODE *MakeKDNode (FLOAT32 Key[], char *Data, int Index); + +void FreeKDNode(KDNODE *Node); + +void Search(int Level, KDNODE *SubTree); + +FLOAT32 ComputeDistance (register int N, +register PARAM_DESC Dim[], +register FLOAT32 p1[], register FLOAT32 p2[]); + +void FindMaxDistance(); + +int QueryIntersectsSearch(); + +int QueryInSearch(); + +void Walk(KDNODE *SubTree, INT32 Level); + +void FreeSubTree(KDNODE *SubTree); +#endif diff --git a/classify/mf.cpp b/classify/mf.cpp new file mode 100644 index 0000000000..3cfdd66170 --- /dev/null +++ b/classify/mf.cpp @@ -0,0 +1,106 @@ +/****************************************************************************** + ** Filename: mf.c + ** Purpose: Micro-feature interface to flexible feature extractor. + ** Author: Dan Johnson + ** History: Thu May 24 09:08:38 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "mfdefs.h" +#include "variables.h" +#include "mf.h" +#include "fxdefs.h" +#include "mfx.h" +#include + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FEATURE_SET ExtractMicros(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract micro-features from + ** LineStats statistics on text row blob is in + ** Globals: none + ** Operation: Call the old micro-feature extractor and then copy + ** the features into the new format. Then deallocate the + ** old micro-features. + ** Return: Micro-features for Blob. + ** Exceptions: none + ** History: Wed May 23 18:06:38 1990, DSJ, Created. + */ + int NumFeatures; + MICROFEATURES Features, OldFeatures; + FEATURE_SET FeatureSet; + FEATURE Feature; + MICROFEATURE OldFeature; + + OldFeatures = (MICROFEATURES) BlobMicroFeatures (Blob, LineStats); + NumFeatures = count (OldFeatures); + FeatureSet = NewFeatureSet (NumFeatures); + + Features = OldFeatures; + iterate(Features) { + OldFeature = (MICROFEATURE) first (Features); + Feature = NewFeature (&MicroFeatureDesc); + ParamOf (Feature, MFDirection) = OrientationOf (OldFeature); + ParamOf (Feature, MFXPosition) = CenterX (OldFeature); + ParamOf (Feature, MFYPosition) = CenterY (OldFeature); + ParamOf (Feature, MFLength) = LengthOf (OldFeature); + + // Bulge features should not be used + // anymore and are therefore set to 0. +// ParamOf (Feature, MFBulge1) = FirstBulgeOf (OldFeature); +// ParamOf (Feature, MFBulge2) = SecondBulgeOf (OldFeature); + ParamOf (Feature, MFBulge1) = 0.0f; + ParamOf (Feature, MFBulge2) = 0.0f; + + AddFeature(FeatureSet, Feature); + } + FreeMicroFeatures(OldFeatures); + return (FeatureSet); + +} /* ExtractMicros */ + + +/*---------------------------------------------------------------------------*/ +void InitMicroFXVars() { +/* + ** Parameters: none + ** Globals: + ** ExtraPenaltyMagnitude controls for adjusting extra penalty + ** ExtraPenaltyWeight + ** ExtraPenaltyOrder + ** Operation: Initialize the microfeature extractor variables that can + ** be tuned without recompiling. + ** Return: none + ** Exceptions: none + ** History: Thu May 24 10:50:46 1990, DSJ, Created. + */ + /* + float_variable (ExtraPenaltyMagnitude, "MFExtraPenaltyMag", + EXTRA_PENALTY_MAGNITUDE); + float_variable (ExtraPenaltyWeight, "MFExtraPenaltyWeight", + EXTRA_PENALTY_WEIGHT); + float_variable (ExtraPenaltyOrder, "MFExtraPenaltyOrder", + EXTRA_PENALTY_ORDER); + */ + InitMicroFxVars(); + +} /* InitMicroFXVars */ diff --git a/classify/mf.h b/classify/mf.h new file mode 100644 index 0000000000..79f1c8ef5f --- /dev/null +++ b/classify/mf.h @@ -0,0 +1,43 @@ +/****************************************************************************** + ** Filename: mf.h + ** Purpose: Micro-feature interface to flexible feature extractor. + ** Author: Dan Johnson + ** History: Thu May 24 09:39:56 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef MF_H +#define MF_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "ocrfeatures.h" +#include "tessclas.h" +#include "fxdefs.h" + +typedef enum { + MFXPosition, MFYPosition, + MFLength, MFDirection, MFBulge1, MFBulge2 +} MF_PARAM_NAME; +/*---------------------------------------------------------------------------- + Private Function Prototypes +-----------------------------------------------------------------------------*/ +FEATURE_SET ExtractMicros(TBLOB *Blob, LINE_STATS *LineStats); + +void InitMicroFXVars(); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern FEATURE_DESC_STRUCT MicroFeatureDesc; +#endif diff --git a/classify/mfdefs.cpp b/classify/mfdefs.cpp new file mode 100644 index 0000000000..34e1ea6e5b --- /dev/null +++ b/classify/mfdefs.cpp @@ -0,0 +1,58 @@ +/****************************************************************************** + ** Filename: mfdefs.c + ** Purpose: Basic routines for manipulating micro-features + ** Author: Dan Johnson + ** History: Mon Jan 22 08:48:58 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "mfdefs.h" +#include "emalloc.h" +#include + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +MICROFEATURE NewMicroFeature() { +/* + ** Parameters: none + ** Globals: none + ** Operation: + ** This routine allocates and returns a new micro-feature + ** data structure. + ** Return: New micro-feature. + ** Exceptions: none + ** History: 7/27/89, DSJ, Created. + */ + return ((MICROFEATURE) Emalloc (sizeof (MFBLOCK))); +} /* NewMicroFeature */ + + +/*---------------------------------------------------------------------------*/ +void FreeMicroFeatures(MICROFEATURES MicroFeatures) { +/* + ** Parameters: + ** MicroFeatures list of micro-features to be freed + ** Globals: none + ** Operation: + ** This routine deallocates all of the memory consumed by + ** a list of micro-features. + ** Return: none + ** Exceptions: none + ** History: 7/27/89, DSJ, Created. + */ + destroy_nodes(MicroFeatures, Efree); +} /* FreeMicroFeatures */ diff --git a/classify/mfdefs.h b/classify/mfdefs.h new file mode 100644 index 0000000000..b0873b34bb --- /dev/null +++ b/classify/mfdefs.h @@ -0,0 +1,67 @@ +/****************************************************************************** + ** Filename: mfdefs.h + ** Purpose: Definition of micro-features + ** Author: Dan Johnson + ** History: Mon Jan 22 08:42:13 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef MFDEFS_H +#define MFDEFS_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "oldlist.h" +#include "matchdefs.h" +#include "xform2d.h" + +/* maximum size of a bulge for length=1 is sqrt(2)/3 */ +#define BULGENORMALIZER 0.942809041 + +/* definition of a list of micro-features */ +typedef LIST MICROFEATURES; + +/* definition of structure of micro-features */ +#define MFSIZE 6 +typedef FLOAT32 MFBLOCK[MFSIZE]; +typedef FLOAT32 *MICROFEATURE; + +/* definitions of individual micro-feature parameters */ +#define XPOSITION 0 +#define YPOSITION 1 +#define MFLENGTH 2 +#define ORIENTATION 3 +#define FIRSTBULGE 4 +#define SECONDBULGE 5 + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* macros for accessing micro-feature parameters */ +#define CenterX(M) ( (M)[XPOSITION] ) +#define CenterY(M) ( (M)[YPOSITION] ) +#define LengthOf(M) ( (M)[MFLENGTH] ) +#define OrientationOf(M) ( (M)[ORIENTATION] ) +#define FirstBulgeOf(M) ( (M)[FIRSTBULGE] ) +#define SecondBulgeOf(M) ( (M)[SECONDBULGE] ) + +/* macros for accessing micro-feature lists */ +#define NextFeatureOf(L) ( (MICROFEATURE) first( L ) ) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +MICROFEATURE NewMicroFeature(); + +void FreeMicroFeatures(MICROFEATURES MicroFeatures); +#endif diff --git a/classify/mfoutline.cpp b/classify/mfoutline.cpp new file mode 100644 index 0000000000..8ea44bf161 --- /dev/null +++ b/classify/mfoutline.cpp @@ -0,0 +1,1086 @@ +/****************************************************************************** + ** Filename: mfoutline.c + ** Purpose: Interface to outline struct used for extracting features + ** Author: Dan Johnson + ** History: Thu May 17 08:14:18 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "clusttool.h" //If remove you get cought in a loop somewhere +#include "emalloc.h" +#include "mfoutline.h" +#include "debug.h" +#include "hideedge.h" +#include "blobs.h" +#include "const.h" +#include "mfx.h" + +#include +#include + +#define MIN_INERTIA (0.00001) + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/mfoutline.c +void ChangeDirection + _ARGS((MFOUTLINE Start, + MFOUTLINE End, + DIRECTION Direction)); + +void CharNormalizeOutline + _ARGS((MFOUTLINE Outline, + OUTLINE_STATS *OutlineStats)); + +void ComputeDirection + _ARGS((MFEDGEPT *Start, + MFEDGEPT *Finish, + FLOAT32 MinSlope, + FLOAT32 MaxSlope)); + +void FinishOutlineStats + _ARGS((OUTLINE_STATS *OutlineStats)); + +void InitOutlineStats + _ARGS((OUTLINE_STATS *OutlineStats)); + +MFOUTLINE NextDirectionChange + _ARGS((MFOUTLINE EdgePoint)); + +void UpdateOutlineStats + _ARGS((OUTLINE_STATS *OutlineStats, + FLOAT32 x1, + FLOAT32 y1, + FLOAT32 x2, + FLOAT32 y2)); + +#undef _ARGS +*/ +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* center of current blob being processed - used when "unexpanding" + expanded blobs */ +static TPOINT BlobCenter; + +/* control knobs used to control normalization of outlines */ +make_int_var (NormMethod, character, MakeNormMethod, +15, 10, SetNormMethod, "Normalization Method ...") +/* PREV DEFAULT "baseline" */ +make_float_var (CharNormRange, 0.2, MakeCharNormRange, +15, 11, SetCharNormRange, "Character Normalization Range ...") +make_float_var (MinNormScaleX, 0.0, MakeMinNormScaleX, +15, 12, SetMinNormScaleX, "Min char x-norm scale ...") +/* PREV DEFAULT 0.1 */ +make_float_var (MaxNormScaleX, 0.325, MakeMaxNormScaleX, +15, 13, SetMaxNormScaleX, "Max char x-norm scale ...") +/* PREV DEFAULT 0.3 */ +make_float_var (MinNormScaleY, 0.0, MakeMinNormScaleY, +15, 14, SetMinNormScaleY, "Min char y-norm scale ...") +/* PREV DEFAULT 0.1 */ +make_float_var (MaxNormScaleY, 0.325, MakeMaxNormScaleY, +15, 15, SetMaxNormScaleY, "Max char y-norm scale ...") +/* PREV DEFAULT 0.3 */ +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ComputeBlobCenter(TBLOB *Blob, TPOINT *BlobCenter) { +/* + ** Parameters: + ** Blob blob to compute centerpoint of + ** BlobCenter data struct to place results in + ** Globals: none + ** Operation: + ** This routine computes the center point of the specified + ** blob using the bounding box of all top level outlines in the + ** blob. The center point is computed in a coordinate system + ** which is scaled up by VECSCALE from the page coordinate + ** system. + ** Return: none + ** Exceptions: none + ** History: Fri Sep 8 10:45:39 1989, DSJ, Created. + */ + TPOINT TopLeft; + TPOINT BottomRight; + + blob_bounding_box(Blob, &TopLeft, &BottomRight); + + BlobCenter->x = ((TopLeft.x << VECSCALE) + (BottomRight.x << VECSCALE)) / 2; + BlobCenter->y = ((TopLeft.y << VECSCALE) + (BottomRight.y << VECSCALE)) / 2; + +} /* ComputeBlobCenter */ + + +/*---------------------------------------------------------------------------*/ +LIST ConvertBlob(TBLOB *Blob) { +/* + ** Parameters: + ** Blob blob to be converted + ** Globals: none + ** Operation: Convert Blob into a list of outlines. + ** Return: List of outlines representing blob. + ** Exceptions: none + ** History: Thu Dec 13 15:40:17 1990, DSJ, Created. + */ + LIST ConvertedOutlines = NIL; + + if (Blob != NULL) { + SettupBlobConversion(Blob); //ComputeBlobCenter (Blob, &BlobCenter); + ConvertedOutlines = ConvertOutlines (Blob->outlines, + ConvertedOutlines, outer); + } + + return (ConvertedOutlines); +} /* ConvertBlob */ + + +/*---------------------------------------------------------------------------*/ +MFOUTLINE ConvertOutline(TESSLINE *Outline) { +/* + ** Parameters: + ** Outline outline to be converted + ** Globals: + ** BlobCenter pre-computed center of current blob + ** Operation: + ** This routine converts the specified outline into a special + ** data structure which is used for extracting micro-features. + ** If the outline has been pre-normalized by the splitter, + ** then it is assumed to be in expanded form and all we must + ** do is copy the points. Otherwise, + ** if the outline is expanded, then the expanded form is used + ** and the coordinates of the points are returned to page + ** coordinates using the global variable BlobCenter and the + ** scaling factor REALSCALE. If the outline is not expanded, + ** then the compressed form is used. + ** Return: Outline converted into special micro-features format. + ** Exceptions: none + ** History: 8/2/89, DSJ, Created. + ** 9/8/89, DSJ, Added ability to convert expanded blobs. + ** 1/11/90, DSJ, Changed to use REALSCALE instead of VECSCALE + ** to eliminate round-off problems. + ** 2/21/91, DSJ, Added ability to work with pre-normalized + ** blobs. + ** 4/30/91, DSJ, Added concept of "hidden" segments. + */ + register BYTEVEC *Vector; + TPOINT Position; + TPOINT StartPosition; + MFEDGEPT *NewPoint; + MFOUTLINE MFOutline = NIL; + EDGEPT *EdgePoint; + EDGEPT *StartPoint; + EDGEPT *NextPoint; + + if (Outline == NULL || + (Outline->compactloop == NULL && Outline->loop == NULL)) + return (MFOutline); + + /* have outlines been prenormalized */ + if (is_baseline_normalized ()) { + StartPoint = Outline->loop; + EdgePoint = StartPoint; + do { + NextPoint = EdgePoint->next; + + /* filter out duplicate points */ + if (EdgePoint->pos.x != NextPoint->pos.x || + EdgePoint->pos.y != NextPoint->pos.y) { + NewPoint = NewEdgePoint (); + ClearMark(NewPoint); + IsHidden (NewPoint) = is_hidden_edge (EdgePoint) ? TRUE : FALSE; + XPositionOf (NewPoint) = EdgePoint->pos.x; + YPositionOf (NewPoint) = EdgePoint->pos.y; + MFOutline = push (MFOutline, NewPoint); + } + EdgePoint = NextPoint; + } + while (EdgePoint != StartPoint); + } + /* use compressed version of outline */ + else if (Outline->loop == NULL) { + Xof (Position) = Xof (StartPosition) = Outline->start.x; + Yof (Position) = Yof (StartPosition) = Outline->start.y; + Vector = Outline->compactloop; + do { + if (Vector->dx != 0 || Vector->dy != 0) { + NewPoint = NewEdgePoint (); + ClearMark(NewPoint); + /* all edges are visible */ + IsHidden (NewPoint) = FALSE; + CopyPoint (Position, PositionOf (NewPoint)); + MFOutline = push (MFOutline, NewPoint); + } + Xof (Position) += Vector->dx; + Yof (Position) += Vector->dy; + Vector++; + } + while ((Xof (Position) != Xof (StartPosition)) || + (Yof (Position) != Yof (StartPosition))); + } + else { /* use expanded version of outline */ + StartPoint = Outline->loop; + EdgePoint = StartPoint; + do { + NextPoint = EdgePoint->next; + + /* filter out duplicate points */ + if (EdgePoint->pos.x != NextPoint->pos.x || + EdgePoint->pos.y != NextPoint->pos.y) { + NewPoint = NewEdgePoint (); + ClearMark(NewPoint); + IsHidden (NewPoint) = is_hidden_edge (EdgePoint) ? TRUE : FALSE; + XPositionOf (NewPoint) = + (EdgePoint->pos.x + BlobCenter.x) / REALSCALE; + YPositionOf (NewPoint) = + (EdgePoint->pos.y + BlobCenter.y) / REALSCALE; + MFOutline = push (MFOutline, NewPoint); + } + EdgePoint = NextPoint; + } + while (EdgePoint != StartPoint); + } + + MakeOutlineCircular(MFOutline); + return (MFOutline); + +} /* ConvertOutline */ + + +/*---------------------------------------------------------------------------*/ +LIST ConvertOutlines(TESSLINE *Outline, + LIST ConvertedOutlines, + OUTLINETYPE OutlineType) { +/* + ** Parameters: + ** Outline first outline to be converted + ** ConvertedOutlines list to add converted outlines to + ** OutlineType are the outlines outer or holes? + ** Globals: none + ** Operation: + ** This routine converts all given outlines into a new format. + ** of outlines. Outline points to a list of the top level + ** outlines to be converted. The children of these outlines + ** are also recursively converted. All converted outlines + ** are added to ConvertedOutlines. This is a list of outlines, + ** one for each outline that was converted. + ** Return: Updated list of converted outlines. + ** Exceptions: none + ** History: Thu Dec 13 15:57:38 1990, DSJ, Created. + */ + MFOUTLINE MFOutline; + + while (Outline != NULL) { + if (Outline->child != NULL) + if (OutlineType == outer) + ConvertedOutlines = ConvertOutlines (Outline->child, + ConvertedOutlines, hole); + else + ConvertedOutlines = ConvertOutlines (Outline->child, + ConvertedOutlines, outer); + + MFOutline = ConvertOutline (Outline); + ConvertedOutlines = push (ConvertedOutlines, MFOutline); + Outline = Outline->next; + } + return (ConvertedOutlines); +} /* ConvertOutlines */ + + +/*---------------------------------------------------------------------------*/ +void ComputeOutlineStats(LIST Outlines, OUTLINE_STATS *OutlineStats) { +/* + ** Parameters: + ** Outlines list of outlines to compute stats for + ** OutlineStats place to put results + ** Globals: none + ** Operation: This routine computes several statistics about the outlines + ** in Outlines. These statistics are usually used to perform + ** anistropic normalization of all of the outlines. The + ** statistics generated are: + ** first moments about x and y axes + ** total length of all outlines + ** center of mass of all outlines + ** second moments about center of mass axes + ** radius of gyration about center of mass axes + ** Return: none (results are returned in OutlineStats) + ** Exceptions: none + ** History: Fri Dec 14 08:32:03 1990, DSJ, Created. + */ + MFOUTLINE Outline; + MFOUTLINE EdgePoint; + MFEDGEPT *Current; + MFEDGEPT *Last; + + InitOutlineStats(OutlineStats); + iterate(Outlines) { + Outline = (MFOUTLINE) first (Outlines); + + Last = PointAt (Outline); + Outline = NextPointAfter (Outline); + EdgePoint = Outline; + do { + Current = PointAt (EdgePoint); + + UpdateOutlineStats (OutlineStats, + XPositionOf (Last), YPositionOf (Last), + XPositionOf (Current), YPositionOf (Current)); + + Last = Current; + EdgePoint = NextPointAfter (EdgePoint); + } + while (EdgePoint != Outline); + } + FinishOutlineStats(OutlineStats); + +} /* ComputeOutlineStats */ + + +/*---------------------------------------------------------------------------*/ +void FilterEdgeNoise(MFOUTLINE Outline, FLOAT32 NoiseSegmentLength) { +/* + ** Parameters: + ** Outline outline to be filtered + ** NoiseSegmentLength maximum length of a "noise" segment + ** Globals: none + ** Operation: Filter out noise from the specified outline. This is + ** done by changing the direction of short segments of the + ** outline to the same direction as the preceding outline + ** segment. + ** Return: none + ** Exceptions: none + ** History: Fri May 4 10:23:45 1990, DSJ, Created. + */ + MFOUTLINE Current; + MFOUTLINE Last; + MFOUTLINE First; + FLOAT32 Length; + int NumFound = 0; + DIRECTION DirectionOfFirst = north; + + if (DegenerateOutline (Outline)) + return; + + /* find 2 segments of different orientation which are long enough to + not be filtered. If two cannot be found, leave the outline unchanged. */ + First = NextDirectionChange (Outline); + Last = First; + do { + Current = NextDirectionChange (Last); + Length = DistanceBetween (PositionOf (PointAt (Current)), + PositionOf (PointAt (Last))); + if (Length >= NoiseSegmentLength) { + if (NumFound == 0) { + NumFound = 1; + DirectionOfFirst = DirectionOf (PointAt (Last)); + } + else if (DirectionOfFirst != DirectionOf (PointAt (Last))) + break; + } + Last = Current; + } + while (Last != First); + if (Current == Last) + return; + + /* find each segment and filter it out if it is too short. Note that + the above code guarantees that the initial direction change will + not be removed, therefore the loop will terminate. */ + First = Last; + do { + Current = NextDirectionChange (Last); + Length = DistanceBetween (PositionOf (PointAt (Current)), + PositionOf (PointAt (Last))); + if (Length < NoiseSegmentLength) + ChangeDirection (Last, Current, PreviousDirectionOf (PointAt (Last))); + + Last = Current; + } + while (Last != First); + +} /* FilterEdgeNoise */ + + +/*---------------------------------------------------------------------------*/ +void FindDirectionChanges(MFOUTLINE Outline, + FLOAT32 MinSlope, + FLOAT32 MaxSlope) { +/* + ** Parameters: + ** Outline micro-feature outline to analyze + ** MinSlope controls "snapping" of segments to horizontal + ** MaxSlope controls "snapping" of segments to vertical + ** Globals: none + ** Operation: + ** This routine searches thru the specified outline, computes + ** a slope for each vector in the outline, and marks each + ** vector as having one of the following directions: + ** N, S, E, W, NE, NW, SE, SW + ** This information is then stored in the outline and the + ** outline is returned. + ** Return: none + ** Exceptions: none + ** History: 7/21/89, DSJ, Created. + */ + MFEDGEPT *Current; + MFEDGEPT *Last; + MFOUTLINE EdgePoint; + + if (DegenerateOutline (Outline)) + return; + + Last = PointAt (Outline); + Outline = NextPointAfter (Outline); + EdgePoint = Outline; + do { + Current = PointAt (EdgePoint); + ComputeDirection(Last, Current, MinSlope, MaxSlope); + + Last = Current; + EdgePoint = NextPointAfter (EdgePoint); + } + while (EdgePoint != Outline); + +} /* FindDirectionChanges */ + + +/*---------------------------------------------------------------------------*/ +void FreeMFOutline(void *arg) { //MFOUTLINE Outline) +/* + ** Parameters: + ** Outline micro-feature outline to be freed + ** Globals: none + ** Operation: + ** This routine deallocates all of the memory consumed by + ** a micro-feature outline. + ** Return: none + ** Exceptions: none + ** History: 7/27/89, DSJ, Created. + */ + MFOUTLINE Start; + MFOUTLINE Outline = (MFOUTLINE) arg; + + /* break the circular outline so we can use std. techniques to deallocate */ + Start = rest (Outline); + set_rest(Outline, NIL); + while (Start != NULL) { + c_free_struct (first (Start), sizeof (MFEDGEPT), "MFEDGEPT"); + Start = pop (Start); + } + +} /* FreeMFOutline */ + + +/*---------------------------------------------------------------------------*/ +void FreeOutlines(LIST Outlines) { +/* + ** Parameters: + ** Outlines list of mf-outlines to be freed + ** Globals: none + ** Operation: Release all memory consumed by the specified list + ** of outlines. + ** Return: none + ** Exceptions: none + ** History: Thu Dec 13 16:14:50 1990, DSJ, Created. + */ + destroy_nodes(Outlines, FreeMFOutline); +} /* FreeOutlines */ + + +/*---------------------------------------------------------------------------*/ +void InitMFOutlineVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine initializes the global control knobs for + ** all routines in this file. + ** Return: none + ** Exceptions: none + ** History: Fri Dec 14 10:50:12 1990, DSJ, Created. + */ + MakeNormMethod(); + MakeCharNormRange(); + MakeMinNormScaleX(); + MakeMaxNormScaleX(); + MakeMinNormScaleY(); + MakeMaxNormScaleY(); +} /* InitMFOutlineVars */ + + +/*---------------------------------------------------------------------------*/ +void MarkDirectionChanges(MFOUTLINE Outline) { +/* + ** Parameters: + ** Outline micro-feature outline to analyze + ** Globals: none + ** Operation: + ** This routine searches thru the specified outline and finds + ** the points at which the outline changes direction. These + ** points are then marked as "extremities". This routine is + ** used as an alternative to FindExtremities(). It forces the + ** endpoints of the microfeatures to be at the direction + ** changes rather than at the midpoint between direction + ** changes. + ** Return: none + ** Exceptions: none + ** History: 6/29/90, DSJ, Created. + */ + MFOUTLINE Current; + MFOUTLINE Last; + MFOUTLINE First; + + if (DegenerateOutline (Outline)) + return; + + First = NextDirectionChange (Outline); + Last = First; + do { + Current = NextDirectionChange (Last); + MarkPoint (PointAt (Current)); + Last = Current; + } + while (Last != First); + +} /* MarkDirectionChanges */ + + +/*---------------------------------------------------------------------------*/ +MFEDGEPT *NewEdgePoint() { +/* + ** Parameters: none + ** Globals: none + ** Operation: + ** This routine allocates and returns a new edge point for + ** a micro-feature outline. + ** Return: New edge point. + ** Exceptions: none + ** History: 7/21/89, DSJ, Created. + */ + return ((MFEDGEPT *) c_alloc_struct (sizeof (MFEDGEPT), "MFEDGEPT")); + +} /* NewEdgePoint */ + + +/*---------------------------------------------------------------------------*/ +MFOUTLINE NextExtremity(MFOUTLINE EdgePoint) { +/* + ** Parameters: + ** EdgePoint start search from this point + ** Globals: none + ** Operation: + ** This routine returns the next point in the micro-feature + ** outline that is an extremity. The search starts after + ** EdgePoint. The routine assumes that the outline being + ** searched is not a degenerate outline (i.e. it must have + ** 2 or more edge points). + ** Return: Next extremity in the outline after EdgePoint. + ** Exceptions: none + ** History: 7/26/89, DSJ, Created. + */ + EdgePoint = NextPointAfter (EdgePoint); + while (NotExtremity (PointAt (EdgePoint))) + EdgePoint = NextPointAfter (EdgePoint); + + return (EdgePoint); + +} /* NextExtremity */ + + +/*---------------------------------------------------------------------------*/ +void NormalizeOutline(MFOUTLINE Outline, + LINE_STATS *LineStats, + FLOAT32 XOrigin) { +/* + ** Parameters: + ** Outline outline to be normalized + ** LineStats statistics for text line normalization + ** XOrigin x-origin of text + ** Globals: none + ** Operation: + ** This routine normalizes the coordinates of the specified + ** outline so that the outline is deskewed down to the + ** baseline, translated so that x=0 is at XOrigin, and scaled + ** so that the height of a character cell from descender to + ** ascender is 1. Of this height, 0.25 is for the descender, + ** 0.25 for the ascender, and 0.5 for the x-height. The + ** y coordinate of the baseline is 0. + ** Return: none + ** Exceptions: none + ** History: 8/2/89, DSJ, Created. + ** 10/23/89, DSJ, Added ascender/descender stretching. + ** 11/89, DSJ, Removed ascender/descender stretching. + */ + MFEDGEPT *Current; + MFOUTLINE EdgePoint; + FLOAT32 ScaleFactor; + FLOAT32 AscStretch; + FLOAT32 DescStretch; + + if (Outline != NIL) { + ScaleFactor = ComputeScaleFactor (LineStats); + AscStretch = 1.0; + DescStretch = 1.0; + + EdgePoint = Outline; + do { + Current = PointAt (EdgePoint); + + YPositionOf (Current) = ScaleFactor * + (YPositionOf (Current) - + BaselineAt (LineStats, XPositionOf (Current))); + + if (YPositionOf (Current) > NORMAL_X_HEIGHT) + YPositionOf (Current) = NORMAL_X_HEIGHT + + (YPositionOf (Current) - NORMAL_X_HEIGHT) / AscStretch; + + else if (YPositionOf (Current) < NORMAL_BASELINE) + YPositionOf (Current) = NORMAL_BASELINE + + (YPositionOf (Current) - NORMAL_BASELINE) / DescStretch; + + XPositionOf (Current) = ScaleFactor * + (XPositionOf (Current) - XOrigin); + + EdgePoint = NextPointAfter (EdgePoint); + } + while (EdgePoint != Outline); + } +} /* NormalizeOutline */ + + +/*---------------------------------------------------------------------------*/ +void NormalizeOutlines(LIST Outlines, + LINE_STATS *LineStats, + FLOAT32 *XScale, + FLOAT32 *YScale) { +/* + ** Parameters: + ** Outlines list of outlines to be normalized + ** LineStats statistics for text line normalization + ** XScale x-direction scale factor used by routine + ** YScale y-direction scale factor used by routine + ** Globals: + ** NormMethod method being used for normalization + ** CharNormRange map radius of gyration to this value + ** Operation: This routine normalizes every outline in Outlines + ** according to the currently selected normalization method. + ** It also returns the scale factors that it used to do this + ** scaling. The scale factors returned represent the x and + ** y sizes in the normalized coordinate system that correspond + ** to 1 pixel in the original coordinate system. + ** Return: none (Outlines are changed and XScale and YScale are updated) + ** Exceptions: none + ** History: Fri Dec 14 08:14:55 1990, DSJ, Created. + */ + MFOUTLINE Outline; + OUTLINE_STATS OutlineStats; + FLOAT32 BaselineScale; + + switch (NormMethod) { + case character: + ComputeOutlineStats(Outlines, &OutlineStats); + + /* limit scale factor to avoid overscaling small blobs (.,`'), + thin blobs (l1ift), and merged blobs */ + *XScale = *YScale = BaselineScale = ComputeScaleFactor (LineStats); + *XScale *= OutlineStats.Ry; + *YScale *= OutlineStats.Rx; + if (*XScale < MinNormScaleX) + *XScale = MinNormScaleX; + if (*YScale < MinNormScaleY) + *YScale = MinNormScaleY; + if (*XScale > MaxNormScaleX && *YScale <= MaxNormScaleY) + *XScale = MaxNormScaleX; + *XScale = CharNormRange * BaselineScale / *XScale; + *YScale = CharNormRange * BaselineScale / *YScale; + + iterate(Outlines) { + Outline = (MFOUTLINE) first (Outlines); + CharNormalizeOutline (Outline, + OutlineStats.x, OutlineStats.y, + *XScale, *YScale); + } + break; + + case baseline: + iterate(Outlines) { + Outline = (MFOUTLINE) first (Outlines); + NormalizeOutline (Outline, LineStats, 0.0); + } + *XScale = *YScale = ComputeScaleFactor (LineStats); + break; + } +} /* NormalizeOutlines */ + + +/*---------------------------------------------------------------------------*/ +void SettupBlobConversion(TBLOB *Blob) { +/* + ** Parameters: + ** Blob blob that is to be converted + ** Globals: + ** BlobCenter center of blob to be converted + ** Operation: Compute the center of the blob's bounding box and save + ** it in a global variable. This routine must be called before + ** any calls to ConvertOutline. It must be called once per + ** blob. + ** Return: none + ** Exceptions: none + ** History: Thu May 17 11:06:17 1990, DSJ, Created. + */ + ComputeBlobCenter(Blob, &BlobCenter); + +} /* SettupBlobConversion */ + + +/*---------------------------------------------------------------------------*/ +void SmearExtremities(MFOUTLINE Outline, FLOAT32 XScale, FLOAT32 YScale) { +/* + ** Parameters: + ** Outline outline whose extremities are to be smeared + ** XScale factor used to normalize outline in x dir + ** YScale factor used to normalize outline in y dir + ** Globals: none + ** Operation: + ** This routine smears the extremities of the specified outline. + ** It does this by adding a random number between + ** -0.5 and 0.5 pixels (that is why X/YScale are needed) to + ** the x and y position of the point. This is done so that + ** the discrete nature of the original scanned image does not + ** affect the statistical clustering used during training. + ** Return: none + ** Exceptions: none + ** History: 1/11/90, DSJ, Created. + */ + MFEDGEPT *Current; + MFOUTLINE EdgePoint; + FLOAT32 MinXSmear; + FLOAT32 MaxXSmear; + FLOAT32 MinYSmear; + FLOAT32 MaxYSmear; + + if (Outline != NIL) { + MinXSmear = -0.5 * XScale; + MaxXSmear = 0.5 * XScale; + MinYSmear = -0.5 * YScale; + MaxYSmear = 0.5 * YScale; + EdgePoint = Outline; + do { + Current = PointAt (EdgePoint); + if (IsExtremity (Current)) { + XPositionOf (Current) += + UniformRandomNumber(MinXSmear, MaxXSmear); + YPositionOf (Current) += + UniformRandomNumber(MinYSmear, MaxYSmear); + } + + EdgePoint = NextPointAfter (EdgePoint); + } + while (EdgePoint != Outline); + } +} /* SmearExtremities */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ChangeDirection(MFOUTLINE Start, MFOUTLINE End, DIRECTION Direction) { +/* + ** Parameters: + ** Start, End defines segment of outline to be modified + ** Direction new direction to assign to segment + ** Globals: none + ** Operation: Change the direction of every vector in the specified + ** outline segment to Direction. The segment to be changed + ** starts at Start and ends at End. Note that the previous + ** direction of End must also be changed to reflect the + ** change in direction of the point before it. + ** Return: none + ** Exceptions: none + ** History: Fri May 4 10:42:04 1990, DSJ, Created. + */ + MFOUTLINE Current; + + for (Current = Start; Current != End; Current = NextPointAfter (Current)) + DirectionOf (PointAt (Current)) = Direction; + + PreviousDirectionOf (PointAt (End)) = Direction; + +} /* ChangeDirection */ + + +/*---------------------------------------------------------------------------*/ +void CharNormalizeOutline(MFOUTLINE Outline, + FLOAT32 XCenter, + FLOAT32 YCenter, + FLOAT32 XScale, + FLOAT32 YScale) { +/* + ** Parameters: + ** Outline outline to be character normalized + ** XCenter, YCenter center point for normalization + ** XScale, YScale scale factors for normalization + ** Globals: none + ** Operation: This routine normalizes each point in Outline by + ** translating it to the specified center and scaling it + ** anisotropically according to the given scale factors. + ** Return: none + ** Exceptions: none + ** History: Fri Dec 14 10:27:11 1990, DSJ, Created. + */ + MFOUTLINE First, Current; + MFEDGEPT *CurrentPoint; + + if (Outline == NIL) + return; + + First = Outline; + Current = First; + do { + CurrentPoint = PointAt (Current); + XPositionOf (CurrentPoint) = + (XPositionOf (CurrentPoint) - XCenter) * XScale; + YPositionOf (CurrentPoint) = + (YPositionOf (CurrentPoint) - YCenter) * YScale; + + Current = NextPointAfter (Current); + } + while (Current != First); + +} /* CharNormalizeOutline */ + + +/*---------------------------------------------------------------------------*/ +void ComputeDirection(MFEDGEPT *Start, + MFEDGEPT *Finish, + FLOAT32 MinSlope, + FLOAT32 MaxSlope) { +/* + ** Parameters: + ** Start starting point to compute direction from + ** Finish finishing point to compute direction to + ** MinSlope slope below which lines are horizontal + ** MaxSlope slope above which lines are vertical + ** Globals: none + ** Operation: + ** This routine computes the slope from Start to Finish and + ** and then computes the approximate direction of the line + ** segment from Start to Finish. The direction is quantized + ** into 8 buckets: + ** N, S, E, W, NE, NW, SE, SW + ** Both the slope and the direction are then stored into + ** the appropriate fields of the Start edge point. The + ** direction is also stored into the PreviousDirection field + ** of the Finish edge point. + ** Return: none + ** Exceptions: none + ** History: 7/25/89, DSJ, Created. + */ + FVECTOR Delta; + + Delta.x = Finish->Point.x - Start->Point.x; + Delta.y = Finish->Point.y - Start->Point.y; + if (Delta.x == 0) + if (Delta.y < 0) { + Start->Slope = -MAX_FLOAT32; + Start->Direction = south; + } + else { + Start->Slope = MAX_FLOAT32; + Start->Direction = north; + } + else { + Start->Slope = Delta.y / Delta.x; + if (Delta.x > 0) + if (Delta.y > 0) + if (Start->Slope > MinSlope) + if (Start->Slope < MaxSlope) + Start->Direction = northeast; + else + Start->Direction = north; + else + Start->Direction = east; + else if (Start->Slope < -MinSlope) + if (Start->Slope > -MaxSlope) + Start->Direction = southeast; + else + Start->Direction = south; + else + Start->Direction = east; + else if (Delta.y > 0) + if (Start->Slope < -MinSlope) + if (Start->Slope > -MaxSlope) + Start->Direction = northwest; + else + Start->Direction = north; + else + Start->Direction = west; + else if (Start->Slope > MinSlope) + if (Start->Slope < MaxSlope) + Start->Direction = southwest; + else + Start->Direction = south; + else + Start->Direction = west; + } + Finish->PreviousDirection = Start->Direction; +} /* ComputeDirection */ + + +/*---------------------------------------------------------------------------*/ +void FinishOutlineStats(register OUTLINE_STATS *OutlineStats) { +/* + ** Parameters: + ** OutlineStats statistics about a set of outlines + ** Globals: none + ** Operation: Use the preliminary statistics accumulated in OutlineStats + ** to compute the final statistics. + ** (see Dan Johnson's Tesseract lab + ** notebook #2, pgs. 74-78). + ** Return: none + ** Exceptions: none + ** History: Fri Dec 14 10:13:36 1990, DSJ, Created. + */ + OutlineStats->x = 0.5 * OutlineStats->My / OutlineStats->L; + OutlineStats->y = 0.5 * OutlineStats->Mx / OutlineStats->L; + + OutlineStats->Ix = (OutlineStats->Ix / 3.0 - + OutlineStats->y * OutlineStats->Mx + + OutlineStats->y * OutlineStats->y * OutlineStats->L); + + OutlineStats->Iy = (OutlineStats->Iy / 3.0 - + OutlineStats->x * OutlineStats->My + + OutlineStats->x * OutlineStats->x * OutlineStats->L); + + /* Ix and/or Iy could possibly be negative due to roundoff error */ + if (OutlineStats->Ix < 0.0) + OutlineStats->Ix = MIN_INERTIA; + if (OutlineStats->Iy < 0.0) + OutlineStats->Iy = MIN_INERTIA; + + OutlineStats->Rx = sqrt (OutlineStats->Ix / OutlineStats->L); + OutlineStats->Ry = sqrt (OutlineStats->Iy / OutlineStats->L); + + OutlineStats->Mx *= 0.5; + OutlineStats->My *= 0.5; + +} /* FinishOutlineStats */ + + +/*---------------------------------------------------------------------------*/ +void InitOutlineStats(OUTLINE_STATS *OutlineStats) { +/* + ** Parameters: + ** OutlineStats stats data structure to be initialized + ** Globals: none + ** Operation: Initialize the outline statistics data structure so + ** that it is ready to start accumulating statistics. + ** Return: none + ** Exceptions: none + ** History: Fri Dec 14 08:55:22 1990, DSJ, Created. + */ + OutlineStats->Mx = 0.0; + OutlineStats->My = 0.0; + OutlineStats->L = 0.0; + OutlineStats->x = 0.0; + OutlineStats->y = 0.0; + OutlineStats->Ix = 0.0; + OutlineStats->Iy = 0.0; + OutlineStats->Rx = 0.0; + OutlineStats->Ry = 0.0; +} /* InitOutlineStats */ + + +/*---------------------------------------------------------------------------*/ +MFOUTLINE NextDirectionChange(MFOUTLINE EdgePoint) { +/* + ** Parameters: + ** EdgePoint start search from this point + ** Globals: none + ** Operation: + ** This routine returns the next point in the micro-feature + ** outline that has a direction different than EdgePoint. The + ** routine assumes that the outline being searched is not a + ** degenerate outline (i.e. it must have 2 or more edge points). + ** Return: Point of next direction change in micro-feature outline. + ** Exceptions: none + ** History: 7/25/89, DSJ, Created. + */ + DIRECTION InitialDirection; + + InitialDirection = DirectionOf (PointAt (EdgePoint)); + + do + EdgePoint = NextPointAfter (EdgePoint); + while (DirectionOf (PointAt (EdgePoint)) == InitialDirection); + + return (EdgePoint); +} /* NextDirectionChange */ + + +/*---------------------------------------------------------------------------*/ +void UpdateOutlineStats(register OUTLINE_STATS *OutlineStats, + register FLOAT32 x1, + register FLOAT32 x2, + register FLOAT32 y1, + register FLOAT32 y2) { +/* + ** Parameters: + ** OutlineStats statistics to add this segment to + ** x1, y1, x2, y2 segment to be added to statistics + ** Globals: none + ** Operation: This routine adds the statistics for the specified + ** line segment to OutlineStats. The statistics that are + ** kept are: + ** sum of length of all segments + ** sum of 2*Mx for all segments + ** sum of 2*My for all segments + ** sum of 2*Mx*(y1+y2) - L*y1*y2 for all segments + ** sum of 2*My*(x1+x2) - L*x1*x2 for all segments + ** These numbers, once collected can later be used to easily + ** compute the center of mass, first and second moments, + ** and radii of gyration. (see Dan Johnson's Tesseract lab + ** notebook #2, pgs. 74-78). + ** Return: none + ** Exceptions: none + ** History: Fri Dec 14 08:59:17 1990, DSJ, Created. + */ + register FLOAT64 L; + register FLOAT64 Mx2; + register FLOAT64 My2; + + /* compute length of segment */ + L = sqrt ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + OutlineStats->L += L; + + /* compute 2Mx and 2My components */ + Mx2 = L * (y1 + y2); + My2 = L * (x1 + x2); + OutlineStats->Mx += Mx2; + OutlineStats->My += My2; + + /* compute second moment component */ + OutlineStats->Ix += Mx2 * (y1 + y2) - L * y1 * y2; + OutlineStats->Iy += My2 * (x1 + x2) - L * x1 * x2; + +} /* UpdateOutlineStats */ diff --git a/classify/mfoutline.h b/classify/mfoutline.h new file mode 100644 index 0000000000..d70667ca87 --- /dev/null +++ b/classify/mfoutline.h @@ -0,0 +1,277 @@ +/****************************************************************************** + ** Filename: mfoutline.h + ** Purpose: Interface spec for fx outline structures + ** Author: Dan Johnson + ** History: Thu May 17 08:55:32 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef MFOUTLINE_H +#define MFOUTLINE_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "general.h" +#include "oldlist.h" +#include "fpoint.h" +#include "fxdefs.h" +#include "baseline.h" + +#define NORMAL_X_HEIGHT (0.5) +#define NORMAL_BASELINE (0.0) + +typedef LIST MFOUTLINE; + +typedef enum { + north, south, east, west, northeast, northwest, southeast, southwest +} + + +DIRECTION; +/* +typedef enum +{ +False, True +} +BOOLEAN; +*/ +typedef struct +{ + FPOINT Point; + FLOAT32 Slope; + unsigned Padding:20; + BOOL8 Hidden:TRUE; + BOOL8 ExtremityMark:TRUE; + DIRECTION Direction:4; + DIRECTION PreviousDirection:4; +} + + +MFEDGEPT; + +typedef enum { + outer, hole +} + + +OUTLINETYPE; + +typedef struct +{ + FLOAT64 Mx, My; /* first moment of all outlines */ + FLOAT64 L; /* total length of all outlines */ + FLOAT64 x, y; /* center of mass of all outlines */ + FLOAT64 Ix, Iy; /* second moments about center of mass axes */ + FLOAT64 Rx, Ry; /* radius of gyration about center of mass axes */ +} + + +OUTLINE_STATS; + +typedef enum { + baseline, character +} + + +NORM_METHOD; + +/*---------------------------------------------------------------------------- + Variables +------------------------------------------------------------------------------*/ +extern int NormMethod; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +#define AverageOf(A,B) (((A) + (B)) / 2) + +/* macro for computing the baseline of a row of text at an x position */ +#define BaselineAt(L,X) (BASELINE_OFFSET) + +/* macro for computing the scale factor to use to normalize characters */ +#define ComputeScaleFactor(L) \ +(NORMAL_X_HEIGHT / ((is_baseline_normalized ())? \ + (BASELINE_SCALE): \ + ((L)->xheight))) + +/* macros for manipulating micro-feature outlines */ +#define DegenerateOutline(O) (((O) == NIL) || ((O) == rest(O))) +#define PointAt(O) ((MFEDGEPT *) first (O)) +#define NextPointAfter(E) (rest (E)) +#define MakeOutlineCircular(O) (set_rest (last (O), (O))) + +/* macros for manipulating micro-feature outline edge points */ +#define PositionOf(P) ((P)->Point) +#define XPositionOf(P) (PositionOf(P).x) +#define YPositionOf(P) (PositionOf(P).y) +#define DirectionOf(P) ((P)->Direction) +#define PreviousDirectionOf(P) ((P)->PreviousDirection) +#define ClearMark(P) ((P)->ExtremityMark = FALSE) +#define MarkPoint(P) ((P)->ExtremityMark = TRUE) +#define IsExtremity(P) ((P)->ExtremityMark) +#define NotExtremity(P) (!IsExtremity(P)) +#define IsVisible(E) (! IsHidden(E)) +#define IsHidden(E) ((E)->Hidden) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void ComputeBlobCenter(TBLOB *Blob, TPOINT *BlobCenter); + +LIST ConvertBlob(TBLOB *Blob); + +MFOUTLINE ConvertOutline(TESSLINE *Outline); + +LIST ConvertOutlines(TESSLINE *Outline, + LIST ConvertedOutlines, + OUTLINETYPE OutlineType); + +void ComputeOutlineStats(LIST Outlines, OUTLINE_STATS *OutlineStats); + +void FilterEdgeNoise(MFOUTLINE Outline, FLOAT32 NoiseSegmentLength); + +void FindDirectionChanges(MFOUTLINE Outline, + FLOAT32 MinSlope, + FLOAT32 MaxSlope); + +void FreeMFOutline(void *agr); //MFOUTLINE Outline); + +void FreeOutlines(LIST Outlines); + +void InitMFOutlineVars(); + +void MarkDirectionChanges(MFOUTLINE Outline); + +MFEDGEPT *NewEdgePoint(); + +MFOUTLINE NextExtremity(MFOUTLINE EdgePoint); + +void NormalizeOutline(MFOUTLINE Outline, + LINE_STATS *LineStats, + FLOAT32 XOrigin); + +void NormalizeOutlines(LIST Outlines, + LINE_STATS *LineStats, + FLOAT32 *XScale, + FLOAT32 *YScale); + +void SettupBlobConversion(TBLOB *Blob); + +void SmearExtremities(MFOUTLINE Outline, FLOAT32 XScale, FLOAT32 YScale); + +/*---------------------------------------------------------------------------- + Private Function Prototypes +-----------------------------------------------------------------------------*/ +void ChangeDirection(MFOUTLINE Start, MFOUTLINE End, DIRECTION Direction); + +void CharNormalizeOutline(MFOUTLINE Outline, + FLOAT32 XCenter, + FLOAT32 YCenter, + FLOAT32 XScale, + FLOAT32 YScale); + +void ComputeDirection(MFEDGEPT *Start, + MFEDGEPT *Finish, + FLOAT32 MinSlope, + FLOAT32 MaxSlope); + +void FinishOutlineStats(register OUTLINE_STATS *OutlineStats); + +void InitOutlineStats(OUTLINE_STATS *OutlineStats); + +MFOUTLINE NextDirectionChange(MFOUTLINE EdgePoint); + +void UpdateOutlineStats(register OUTLINE_STATS *OutlineStats, + register FLOAT32 x1, + register FLOAT32 x2, + register FLOAT32 y1, + register FLOAT32 y2); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* mfoutline.c +void ComputeBlobCenter + _ARGS((BLOB *Blob, + TPOINT *BlobCenter)); + +LIST ConvertBlob + _ARGS((BLOB *Blob)); + +MFOUTLINE ConvertOutline + _ARGS((TESSLINE *Outline)); + +LIST ConvertOutlines + _ARGS((TESSLINE *Outline, + LIST ConvertedOutlines, + OUTLINETYPE OutlineType)); + +void ComputeOutlineStats + _ARGS((LIST Outlines, + OUTLINE_STATS *OutlineStats)); + +void FilterEdgeNoise + _ARGS((MFOUTLINE Outline, + FLOAT32 NoiseSegmentLength)); + +void FindDirectionChanges + _ARGS((MFOUTLINE Outline, + FLOAT32 MinSlope, + FLOAT32 MaxSlope)); + +void FreeMFOutline + _ARGS((MFOUTLINE Outline)); + +void FreeOutlines + _ARGS((LIST Outlines)); + +void InitMFOutlineVars + _ARGS((void)); + +void MarkDirectionChanges + _ARGS((MFOUTLINE Outline)); + +MFEDGEPT *NewEdgePoint + _ARGS((void)); + +MFOUTLINE NextExtremity + _ARGS((MFOUTLINE EdgePoint)); + +void NormalizeOutline + _ARGS((MFOUTLINE Outline, + LINE_STATS *LineStats, + FLOAT32 XOrigin)); + +void NormalizeOutlines + _ARGS((LIST Outlines, + LINE_STATS *LineStats)); + +void SettupBlobConversion + _ARGS((BLOB *Blob)); + +void SmearExtremities + _ARGS((MFOUTLINE Outline, + FLOAT32 XScale, + FLOAT32 YScale)); + +#undef _ARGS +*/ +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern int NormMethod; /* normalized method currently selected */ +#endif diff --git a/classify/mfx.cpp b/classify/mfx.cpp new file mode 100644 index 0000000000..4174555844 --- /dev/null +++ b/classify/mfx.cpp @@ -0,0 +1,437 @@ +/****************************************************************************** + ** Filename: mfx.c + ** Purpose: Micro feature extraction routines + ** Author: Dan Johnson + ** History: 7/21/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "mfdefs.h" +#include "variables.h" +#include "sigmenu.h" +#include "mfoutline.h" +#include "clusttool.h" //NEEDED +#include "const.h" +#include "intfx.h" +#include + +/* default values for tunable knobs */ +/* old numbers corresponded to 10.0 degrees and 80.0 degrees */ + /* PREV DEFAULT 0.176326981 approx. 10.0 degrees */ +#define MIN_SLOPE 0.414213562 + /* PREV DEFAULT 5.671281820 approx. 80.0 degrees */ +#define MAX_SLOPE 2.414213562 + /* no noise filtering */ +#define NOISE_SEGMENT_LENGTH (0.00) + /* no feature splitting */ +#define MAX_FEATURE_LENGTH (MAXFLOAT) + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* miscellaneous macros */ +#define NormalizeAngle(A) ( (((A)<0)?((A)+2*PI):(A)) / (2*PI) ) + +/*---------------------------------------------------------------------------- + Private Function Prototypes +-----------------------------------------------------------------------------*/ +void ComputeBulges(MFOUTLINE Start, MFOUTLINE End, MICROFEATURE MicroFeature); + +FLOAT32 ComputeOrientation(MFEDGEPT *Start, MFEDGEPT *End); + +MICROFEATURES ConvertToMicroFeatures(MFOUTLINE Outline, + MICROFEATURES MicroFeatures); + +MICROFEATURE ExtractMicroFeature(MFOUTLINE Start, MFOUTLINE End); + +void SmearBulges(MICROFEATURES MicroFeatures, FLOAT32 XScale, FLOAT32 YScale); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/mfx.c +void ComputeBulges + _ARGS((MFOUTLINE Start, + MFOUTLINE End, + MICROFEATURE MicroFeature)); + +FLOAT32 ComputeOrientation + _ARGS((MFEDGEPT *Start, + MFEDGEPT *End)); + +MICROFEATURES ConvertToMicroFeatures + _ARGS((MFOUTLINE Outline, + MICROFEATURES MicroFeatures)); + +MICROFEATURE ExtractMicroFeature + _ARGS((MFOUTLINE Start, + MFOUTLINE End)); + +void SmearBulges + _ARGS((MICROFEATURES MicroFeatures, + FLOAT32 XScale, + FLOAT32 YScale)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* tuning knobs that can be adjusted without recompilation */ +static FLOAT32 MinSlope; +static FLOAT32 MaxSlope; +static FLOAT32 NoiseSegmentLength; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void InitMicroFxVars() { +/* + ** Parameters: none + ** Globals: + ** MinSlope slope below which lines are called horizontal + ** MaxSlope slope above which lines are called vertical + ** NoiseSegmentLength length below which outline segments + ** are treated as noise + ** MaxFeatureLength length above which a feature will + ** be split into 2 equal pieces + ** ExtremityMode controls how extremities are defined + ** XHeightAdjust allows xheight of line to be adjusted + ** Operation: Initialize the micro-feature extractor variables (knobs) + ** that can be tuned without recompiling. + ** Return: none + ** Exceptions: none + ** History: Mon May 14 11:24:40 1990, DSJ, Created. + */ + VALUE dummy; + + float_variable (MinSlope, "MinSlope", MIN_SLOPE); + float_variable (MaxSlope, "MaxSlope", MAX_SLOPE); + float_variable (NoiseSegmentLength, "NoiseSegmentLength", + NOISE_SEGMENT_LENGTH); +} /* InitMicroFxVars */ + + +/*---------------------------------------------------------------------------*/ +CHAR_FEATURES BlobMicroFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract micro-features from + ** LineStats statistics for text line normalization + ** Globals: + ** XHeightAdjust used for manually adjusting xheight + ** Operation: + ** This routine extracts micro-features from the specified + ** blob and returns a list of the micro-features. All + ** micro-features are normalized according to the specified + ** line statistics. + ** Return: List of micro-features extracted from the blob. + ** Exceptions: none + ** History: 7/21/89, DSJ, Created. + */ + MICROFEATURES MicroFeatures = NIL; + FLOAT32 XScale, YScale; + LIST Outlines; + LIST RemainingOutlines; + MFOUTLINE Outline; + INT_FEATURE_ARRAY blfeatures; + INT_FEATURE_ARRAY cnfeatures; + INT_FX_RESULT_STRUCT results; + + if (Blob != NULL) { + Outlines = ConvertBlob (Blob); +// NormalizeOutlines(Outlines, LineStats, &XScale, &YScale); + ExtractIntFeat(Blob, blfeatures, cnfeatures, &results); + XScale = 0.2f / results.Ry; + YScale = 0.2f / results.Rx; + + RemainingOutlines = Outlines; + iterate(RemainingOutlines) { + Outline = (MFOUTLINE) first (RemainingOutlines); + CharNormalizeOutline (Outline, + results.Xmean, results.Ymean, + XScale, YScale); + } + + RemainingOutlines = Outlines; + iterate(RemainingOutlines) { + Outline = (MFOUTLINE) first (RemainingOutlines); + FindDirectionChanges(Outline, MinSlope, MaxSlope); + FilterEdgeNoise(Outline, NoiseSegmentLength); + MarkDirectionChanges(Outline); + SmearExtremities(Outline, XScale, YScale); + MicroFeatures = ConvertToMicroFeatures (Outline, MicroFeatures); + } + SmearBulges(MicroFeatures, XScale, YScale); + FreeOutlines(Outlines); + } + return ((CHAR_FEATURES) MicroFeatures); +} /* BlobMicroFeatures */ + + +/**---------------------------------------------------------------------------- + Private Macros +----------------------------------------------------------------------------**/ +/********************************************************************** + * angle_of + * + * Return the angle of the line between two points. + **********************************************************************/ +#define angle_of(x1,y1,x2,y2) \ +((x2-x1) ? \ + (atan2 (y2-y1, x2-x1)) : \ + ((y2= 1)) + Orientation = 0; + return (Orientation); +} /* ComputeOrientation */ + + +/*---------------------------------------------------------------------------*/ +MICROFEATURES ConvertToMicroFeatures(MFOUTLINE Outline, + MICROFEATURES MicroFeatures) { +/* + ** Parameters: + ** Outline outline to extract micro-features from + ** MicroFeatures list of micro-features to add to + ** Globals: none + ** Operation: + ** This routine + ** Return: List of micro-features with new features added to front. + ** Exceptions: none + ** History: 7/26/89, DSJ, Created. + */ + MFOUTLINE Current; + MFOUTLINE Last; + MFOUTLINE First; + MICROFEATURE NewFeature; + + if (DegenerateOutline (Outline)) + return (MicroFeatures); + + First = NextExtremity (Outline); + Last = First; + do { + Current = NextExtremity (Last); + NewFeature = ExtractMicroFeature (Last, Current); + if (NewFeature != NULL) + MicroFeatures = push (MicroFeatures, NewFeature); + Last = Current; + } + while (Last != First); + + return (MicroFeatures); +} /* ConvertToMicroFeatures */ + + +/*---------------------------------------------------------------------------*/ +MICROFEATURE ExtractMicroFeature(MFOUTLINE Start, MFOUTLINE End) { +/* + ** Parameters: + ** Start starting point of micro-feature + ** End ending point of micro-feature + ** Globals: none + ** Operation: + ** This routine computes the feature parameters which describe + ** the micro-feature that starts and Start and ends at End. + ** A new micro-feature is allocated, filled with the feature + ** parameters, and returned. The routine assumes that + ** Start and End are not the same point. If they are the + ** same point, NULL is returned, a warning message is + ** printed, and the current outline is dumped to stdout. + ** Return: New micro-feature or NULL if the feature was rejected. + ** Exceptions: none + ** History: 7/26/89, DSJ, Created. + ** 11/17/89, DSJ, Added handling for Start and End same point. + */ + MICROFEATURE NewFeature; + MFEDGEPT *P1, *P2; + + P1 = PointAt (Start); + P2 = PointAt (End); + + NewFeature = NewMicroFeature (); + CenterX (NewFeature) = AverageOf (XPositionOf (P1), XPositionOf (P2)); + CenterY (NewFeature) = AverageOf (YPositionOf (P1), YPositionOf (P2)); + LengthOf (NewFeature) = DistanceBetween (PositionOf (P1), PositionOf (P2)); + OrientationOf (NewFeature) = + NormalizedAngleFrom (&(PositionOf (P1)), &(PositionOf (P2)), 1.0); + ComputeBulges(Start, End, NewFeature); + return (NewFeature); +} /* ExtractMicroFeature */ + + +/*---------------------------------------------------------------------------*/ +void SmearBulges(MICROFEATURES MicroFeatures, FLOAT32 XScale, FLOAT32 YScale) { +/* + ** Parameters: + ** MicroFeatures features to be smeared + ** XScale # of normalized units per pixel in x dir + ** YScale # of normalized units per pixel in y dir + ** Globals: none + ** Operation: Add a random amount to each bulge parameter of each + ** feature. The amount added is between -0.5 pixels and + ** 0.5 pixels. This is done to prevent the prototypes + ** generated in training from being unrealistically tight. + ** Return: none + ** Exceptions: none + ** History: Thu Jun 28 18:03:38 1990, DSJ, Created. + */ + MICROFEATURE MicroFeature; + FLOAT32 MinSmear; + FLOAT32 MaxSmear; + FLOAT32 Cos, Sin; + FLOAT32 Scale; + + iterate(MicroFeatures) { + MicroFeature = NextFeatureOf (MicroFeatures); + + Cos = fabs (cos (2.0 * PI * OrientationOf (MicroFeature))); + Sin = fabs (sin (2.0 * PI * OrientationOf (MicroFeature))); + Scale = YScale * Cos + XScale * Sin; + + MinSmear = -0.5 * Scale / (BULGENORMALIZER * LengthOf (MicroFeature)); + MaxSmear = 0.5 * Scale / (BULGENORMALIZER * LengthOf (MicroFeature)); + + FirstBulgeOf (MicroFeature) += UniformRandomNumber (MinSmear, MaxSmear); + SecondBulgeOf (MicroFeature) += UniformRandomNumber (MinSmear, MaxSmear); + } +} /* SmearBulges */ diff --git a/classify/mfx.h b/classify/mfx.h new file mode 100644 index 0000000000..eabfc40b27 --- /dev/null +++ b/classify/mfx.h @@ -0,0 +1,52 @@ +/****************************************************************************** + ** Filename: mfx.h + ** Purpose: Definition of micro-feature extraction routines + ** Author: Dan Johnson + ** History: 5/29/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef MFX_H +#define MFX_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "fxdefs.h" + +extern FLOAT32 MinSlope; +extern FLOAT32 MaxSlope; +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void InitMicroFxVars(); + +CHAR_FEATURES BlobMicroFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* mfx.c +void InitMicroFxVars + _ARGS((void)); + +CHAR_FEATURES BlobMicroFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats)); + +#undef _ARGS +*/ +#endif diff --git a/classify/normfeat.cpp b/classify/normfeat.cpp new file mode 100644 index 0000000000..a090843ab8 --- /dev/null +++ b/classify/normfeat.cpp @@ -0,0 +1,132 @@ +/****************************************************************************** + ** Filename: normfeat.c + ** Purpose: Definition of char normalization features. + ** Author: Dan Johnson + ** History: 12/14/90, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "normfeat.h" +#include "mfoutline.h" +#include "intfx.h" + +#include "ocrfeatures.h" //Debug +#include //Debug +#include "efio.h" //Debug +//#include "christydbg.h" + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FLOAT32 ActualOutlineLength(FEATURE Feature) { +/* + ** Parameters: + ** Feature normalization feature + ** Globals: none + ** Operation: This routine returns the length that the outline + ** would have been if it were baseline normalized instead + ** of character normalized. + ** Return: Baseline normalized length of outline. + ** Exceptions: none + ** History: Thu Dec 20 14:50:57 1990, DSJ, Created. + */ + return (ParamOf (Feature, CharNormLength) * LENGTH_COMPRESSION); + +} /* ActualOutlineLength */ + + +/*---------------------------------------------------------------------------*/ +FEATURE_SET ExtractCharNormFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract char norm feature from + ** LineStats statistics on text row blob is in + ** Globals: none + ** Operation: Compute a feature whose parameters describe how a + ** character will be affected by the character normalization + ** algorithm. The feature parameters are: + ** y position of center of mass in baseline coordinates + ** total length of outlines in baseline coordinates + ** divided by a scale factor + ** radii of gyration about the center of mass in + ** baseline coordinates + ** Return: Character normalization feature for Blob. + ** Exceptions: none + ** History: Wed May 23 18:06:38 1990, DSJ, Created. + */ + FEATURE_SET FeatureSet; + FEATURE Feature; + FLOAT32 Scale; + FLOAT32 Baseline; + LIST Outlines; + INT_FEATURE_ARRAY blfeatures; + INT_FEATURE_ARRAY cnfeatures; + INT_FX_RESULT_STRUCT FXInfo; + + /* allocate the feature and feature set - note that there is always one + and only one char normalization feature for any blob */ + FeatureSet = NewFeatureSet (1); + Feature = NewFeature (&CharNormDesc); + AddFeature(FeatureSet, Feature); + + /* compute the normalization statistics for this blob */ + Outlines = ConvertBlob (Blob); + /*---------Debug--------------------------------------------------* + OFile = fopen ("f:/ims/debug/nfOutline.logCPP", "r"); + if (OFile == NULL) + { + OFile = Efopen ("f:/ims/debug/nfOutline.logCPP", "w"); + WriteOutlines(OFile, Outlines); + } + else + { + fclose (OFile); + OFile = Efopen ("f:/ims/debug/nfOutline.logCPP", "a"); + } + WriteOutlines(OFile, Outlines); + fclose (OFile); + *--------------------------------------------------------------------*/ + + ExtractIntFeat(Blob, blfeatures, cnfeatures, &FXInfo); + Baseline = BaselineAt (LineStats, FXInfo.Xmean); + Scale = ComputeScaleFactor (LineStats); + ParamOf (Feature, CharNormY) = (FXInfo.Ymean - Baseline) * Scale; + ParamOf (Feature, CharNormLength) = + FXInfo.Length * Scale / LENGTH_COMPRESSION; + ParamOf (Feature, CharNormRx) = FXInfo.Rx * Scale; + ParamOf (Feature, CharNormRy) = FXInfo.Ry * Scale; + + /*---------Debug--------------------------------------------------* + File = fopen ("f:/ims/debug/nfFeatSet.logCPP", "r"); + if (File == NULL) + { + File = Efopen ("f:/ims/debug/nfFeatSet.logCPP", "w"); + WriteFeatureSet(File, FeatureSet); + } + else + { + fclose (File); + File = Efopen ("f:/ims/debug/nfFeatSet.logCPP", "a"); + } + WriteFeatureSet(File, FeatureSet); + fclose (File); + *--------------------------------------------------------------------*/ + FreeOutlines(Outlines); + return (FeatureSet); +} /* ExtractCharNormFeatures */ diff --git a/classify/normfeat.h b/classify/normfeat.h new file mode 100644 index 0000000000..ce2ec70322 --- /dev/null +++ b/classify/normfeat.h @@ -0,0 +1,63 @@ +/****************************************************************************** + ** Filename: normfeat.h + ** Purpose: Definition of character normalization features. + ** Author: Dan Johnson + ** History: 12/14/90, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef NORMFEAT_H +#define NORMFEAT_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "ocrfeatures.h" +#include "tessclas.h" +#include "fxdefs.h" + +#define LENGTH_COMPRESSION (10.0) + +typedef enum +{ CharNormY, CharNormLength, CharNormRx, CharNormRy } +NORM_PARAM_NAME; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +FLOAT32 ActualOutlineLength(FEATURE Feature); + +FEATURE_SET ExtractCharNormFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* normfeat.c +FLOAT32 ActualOutlineLength + _ARGS((FEATURE Feature)); + +FEATURE_SET ExtractCharNormFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern FEATURE_DESC_STRUCT CharNormDesc; +#endif diff --git a/classify/normmatch.cpp b/classify/normmatch.cpp new file mode 100644 index 0000000000..8af6ed41af --- /dev/null +++ b/classify/normmatch.cpp @@ -0,0 +1,300 @@ +/****************************************************************************** + ** Filename: normmatch.c + ** Purpose: Simple matcher based on character normalization features. + ** Author: Dan Johnson + ** History: Wed Dec 19 16:18:06 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "normmatch.h" +#include "clusttool.h" +#include "normfeat.h" +#include "debug.h" +#include "const.h" +#include "efio.h" +#include "emalloc.h" +#include "globals.h" +#include "scanutils.h" + +#include +#include + +/* define default filenames for training data */ +#define NORM_PROTO_FILE "tessdata/normproto" + +typedef struct +{ + int NumParams; + PARAM_DESC *ParamDesc; + LIST Protos[MAX_CLASS_ID + 1]; +} + + +NORM_PROTOS; + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +FLOAT32 NormEvidenceOf(register FLOAT32 NormAdj); + +void PrintNormMatch(FILE *File, + int NumParams, + PROTOTYPE *Proto, + FEATURE Feature); + +NORM_PROTOS *ReadNormProtos(FILE *File); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* global data structure to hold char normalization protos */ +static NORM_PROTOS *NormProtos; + +/* name of file containing char normalization protos */ +static const char *NormProtoFile = NORM_PROTO_FILE; + +/* control knobs used to control the normalization adjustment process */ +make_float_var (NormAdjMidpoint, 32.0, MakeNormAdjMidpoint, +15, 16, SetNormAdjMidpoint, "Norm adjust midpoint ...") +make_float_var (NormAdjCurl, 2.0, MakeNormAdjCurl, +15, 17, SetNormAdjCurl, "Norm adjust curl ...") +//extern char *demodir; +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FLOAT32 ComputeNormMatch(CLASS_ID ClassId, FEATURE Feature, BOOL8 DebugMatch) { +/* + ** Parameters: + ** ClassId id of class to match against + ** Feature character normalization feature + ** DebugMatch controls dump of debug info + ** Globals: + ** NormProtos character normalization prototypes + ** Operation: This routine compares Features against each character + ** normalization proto for ClassId and returns the match + ** rating of the best match. + ** Return: Best match rating for Feature against protos of ClassId. + ** Exceptions: none + ** History: Wed Dec 19 16:56:12 1990, DSJ, Created. + */ + LIST Protos; + FLOAT32 BestMatch; + FLOAT32 Match; + FLOAT32 Delta; + PROTOTYPE *Proto; + int ProtoId; + + /* handle requests for classification as noise */ + if (ClassId == NO_CLASS) { + /* kludge - clean up constants and make into control knobs later */ + Match = (ParamOf (Feature, CharNormLength) * + ParamOf (Feature, CharNormLength) * 500.0 + + ParamOf (Feature, CharNormRx) * + ParamOf (Feature, CharNormRx) * 8000.0 + + ParamOf (Feature, CharNormRy) * + ParamOf (Feature, CharNormRy) * 8000.0); + return (1.0 - NormEvidenceOf (Match)); + } + + BestMatch = MAX_FLOAT32; + Protos = NormProtos->Protos[ClassId]; + + if (DebugMatch) { + cprintf ("\nFeature = "); + WriteFeature(stdout, Feature); + } + + ProtoId = 0; + iterate(Protos) { + Proto = (PROTOTYPE *) first (Protos); + Delta = ParamOf (Feature, CharNormY) - Proto->Mean[CharNormY]; + Match = Delta * Delta * Proto->Weight.Elliptical[CharNormY]; + Delta = ParamOf (Feature, CharNormRx) - Proto->Mean[CharNormRx]; + Match += Delta * Delta * Proto->Weight.Elliptical[CharNormRx]; + + if (Match < BestMatch) + BestMatch = Match; + + if (DebugMatch) { + cprintf ("Proto %1d = ", ProtoId); + WriteNFloats (stdout, NormProtos->NumParams, Proto->Mean); + cprintf (" var = "); + WriteNFloats (stdout, NormProtos->NumParams, + Proto->Variance.Elliptical); + cprintf (" match = "); + PrintNormMatch (stdout, NormProtos->NumParams, Proto, Feature); + } + ProtoId++; + } + return (1.0 - NormEvidenceOf (BestMatch)); +} /* ComputeNormMatch */ + + +/*---------------------------------------------------------------------------*/ +void GetNormProtos() { +/* + ** Parameters: none + ** Globals: + ** NormProtoFile name of file containing normalization protos + ** NormProtos global data structure to hold protos + ** Operation: This routine reads in a set of character normalization + ** protos from NormProtoFile and places them into NormProtos. + ** Return: none + ** Exceptions: none + ** History: Wed Dec 19 16:24:25 1990, DSJ, Created. + */ + FILE *File; + char name[1024]; + + strcpy(name, demodir); + strcat(name, NormProtoFile); + File = Efopen (name, "r"); + NormProtos = ReadNormProtos (File); + fclose(File); + +} /* GetNormProtos */ + +void FreeNormProtos() { + if (NormProtos != NULL) { + for (int i = 0; i <= MAX_CLASS_ID; i++) + FreeProtoList(&NormProtos->Protos[i]); + Efree(NormProtos->ParamDesc); + Efree(NormProtos); + NormProtos = NULL; + } +} + +/*---------------------------------------------------------------------------*/ +void InitNormProtoVars() { +/* + ** Parameters: none + ** Globals: + ** NormProtoFile filename for normalization protos + ** Operation: Initialize the control variables for the normalization + ** matcher. + ** Return: none + ** Exceptions: none + ** History: Mon Nov 5 17:22:10 1990, DSJ, Created. + */ + VALUE dummy; + + string_variable (NormProtoFile, "NormProtoFile", NORM_PROTO_FILE); + + MakeNormAdjMidpoint(); + MakeNormAdjCurl(); + +} /* InitNormProtoVars */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/********************************************************************** + * NormEvidenceOf + * + * Return the new type of evidence number corresponding to this + * normalization adjustment. The equation that represents the transform is: + * 1 / (1 + (NormAdj / midpoint) ^ curl) + **********************************************************************/ +FLOAT32 NormEvidenceOf(register FLOAT32 NormAdj) { + NormAdj /= NormAdjMidpoint; + + if (NormAdjCurl == 3) + NormAdj = NormAdj * NormAdj * NormAdj; + else if (NormAdjCurl == 2) + NormAdj = NormAdj * NormAdj; + else + NormAdj = pow (NormAdj, NormAdjCurl); + return (1.0 / (1.0 + NormAdj)); +} + + +/*---------------------------------------------------------------------------*/ +void PrintNormMatch(FILE *File, + int NumParams, + PROTOTYPE *Proto, + FEATURE Feature) { +/* + ** Parameters: + ** File open text file to dump match debug info to + ** NumParams # of parameters in proto and feature + ** Proto[] array of prototype parameters + ** Feature[] array of feature parameters + ** Globals: none + ** Operation: This routine dumps out detailed normalization match info. + ** Return: none + ** Exceptions: none + ** History: Wed Jan 2 09:49:35 1991, DSJ, Created. + */ + int i; + FLOAT32 ParamMatch; + FLOAT32 TotalMatch; + + for (i = 0, TotalMatch = 0.0; i < NumParams; i++) { + ParamMatch = ((ParamOf (Feature, i) - Mean (Proto, i)) / + StandardDeviation (Proto, i)); + + fprintf (File, " %6.1f", ParamMatch); + + if (i == CharNormY || i == CharNormRx) + TotalMatch += ParamMatch * ParamMatch; + } + fprintf (File, " --> %6.1f (%4.2f)\n", + TotalMatch, NormEvidenceOf (TotalMatch)); + +} /* PrintNormMatch */ + + +/*---------------------------------------------------------------------------*/ +NORM_PROTOS *ReadNormProtos(FILE *File) { +/* + ** Parameters: + ** File open text file to read normalization protos from + ** Globals: none + ** Operation: This routine allocates a new data structure to hold + ** a set of character normalization protos. It then fills in + ** the data structure by reading from the specified File. + ** Return: Character normalization protos. + ** Exceptions: none + ** History: Wed Dec 19 16:38:49 1990, DSJ, Created. + */ + NORM_PROTOS *NormProtos; + int i; + char ClassId[2]; + LIST Protos; + int NumProtos; + + /* allocate and initialization data structure */ + NormProtos = (NORM_PROTOS *) Emalloc (sizeof (NORM_PROTOS)); + for (i = 0; i <= MAX_CLASS_ID; i++) + NormProtos->Protos[i] = NIL; + + /* read file header and save in data structure */ + NormProtos->NumParams = ReadSampleSize (File); + NormProtos->ParamDesc = ReadParamDesc (File, NormProtos->NumParams); + + /* read protos for each class into a separate list */ + while (fscanf (File, "%1s %d", ClassId, &NumProtos) == 2) { + Protos = NormProtos->Protos[ClassId[0]]; + for (i = 0; i < NumProtos; i++) + Protos = + push_last (Protos, ReadPrototype (File, NormProtos->NumParams)); + NormProtos->Protos[ClassId[0]] = Protos; + } + + return (NormProtos); + +} /* ReadNormProtos */ diff --git a/classify/normmatch.h b/classify/normmatch.h new file mode 100644 index 0000000000..a6798e4858 --- /dev/null +++ b/classify/normmatch.h @@ -0,0 +1,59 @@ +/****************************************************************************** + ** Filename: normmatch.h + ** Purpose: Simple matcher based on character normalization features. + ** Author: Dan Johnson + ** History: Thu Dec 20 08:55:05 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef NORMMATCH_H +#define NORMMATCH_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "matchdefs.h" +//#include "cluster.h" +#include "ocrfeatures.h" + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +FLOAT32 ComputeNormMatch(CLASS_ID ClassId, FEATURE Feature, BOOL8 DebugMatch); + +void GetNormProtos(); +void FreeNormProtos(); + +void InitNormProtoVars(); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* normmatch.c * +FLOAT32 ComputeNormMatch + _ARGS((CLASS_ID ClassId, + FEATURE Feature, + BOOL8 DebugMatch)); + +void GetNormProtos + _ARGS((void)); + +void InitNormProtoVars + _ARGS((void)); + +#undef _ARGS +*/ +#endif diff --git a/classify/ocrfeatures.cpp b/classify/ocrfeatures.cpp new file mode 100644 index 0000000000..85a807bb41 --- /dev/null +++ b/classify/ocrfeatures.cpp @@ -0,0 +1,310 @@ +/****************************************************************************** + ** Filename: features.c + ** Purpose: Generic definition of a feature. + ** Author: Dan Johnson + ** History: Mon May 21 10:49:04 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "ocrfeatures.h" +#include "emalloc.h" +#include "callcpp.h" +#include "danerror.h" +#include "freelist.h" +#include "scanutils.h" + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +BOOL8 AddFeature(FEATURE_SET FeatureSet, FEATURE Feature) { +/* + ** Parameters: + ** FeatureSet set of features to add Feature to + ** Feature feature to be added to FeatureSet + ** Globals: none + ** Operation: Add a feature to a feature set. If the feature set is + ** already full, FALSE is returned to indicate that the + ** feature could not be added to the set; otherwise, TRUE is + ** returned. + ** Return: TRUE if feature added to set, FALSE if set is already full. + ** Exceptions: none + ** History: Tue May 22 17:22:23 1990, DSJ, Created. + */ + if (NumFeaturesIn (FeatureSet) >= MaxNumFeaturesIn (FeatureSet)) { + FreeFeature(Feature); + return (FALSE); + } + + FeatureIn (FeatureSet, NumFeaturesIn (FeatureSet)) = Feature; + NumFeaturesIn (FeatureSet)++; + return (TRUE); + +} /* AddFeature */ + + +/*---------------------------------------------------------------------------*/ +void DefaultInitFXVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine can be used by any feature extractor which + ** does not use adjustable controls. + ** It does nothing. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 16:37:45 1990, DSJ, Created. + */ +} /* DefaultInitFXVars */ + + +/*---------------------------------------------------------------------------*/ +void FreeFeature(FEATURE Feature) { +/* + ** Parameters: + ** Feature feature to be deallocated. + ** Globals: none + ** Operation: Release the memory consumed by the specified feature. + ** Return: none + ** Exceptions: none + ** History: Mon May 21 13:33:27 1990, DSJ, Created. + */ + if (Feature) { + c_free_struct (Feature, sizeof (FEATURE_STRUCT) + + sizeof (FLOAT32) * (NumParamsIn (Feature) - 1), + "sizeof(FEATURE_STRUCT)+sizeof(FLOAT32)*(NumParamsIn(Feature)-1)"); + } + +} /* FreeFeature */ + + +/*---------------------------------------------------------------------------*/ +void FreeFeatureSet(FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** FeatureSet set of features to be freed + ** Globals: none + ** Operation: Release the memory consumed by the specified feature + ** set. This routine also frees the memory consumed by the + ** features contained in the set. + ** Return: none + ** Exceptions: none + ** History: Mon May 21 13:59:46 1990, DSJ, Created. + */ + int i; + + if (FeatureSet) { + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) + FreeFeature (FeatureIn (FeatureSet, i)); + memfree(FeatureSet); + } +} /* FreeFeatureSet */ + + +/*---------------------------------------------------------------------------*/ +FEATURE NewFeature(FEATURE_DESC FeatureDesc) { +/* + ** Parameters: + ** FeatureDesc description of feature to be created. + ** Globals: none + ** Operation: Allocate and return a new feature of the specified + ** type. + ** Return: New feature. + ** Exceptions: none + ** History: Mon May 21 14:06:42 1990, DSJ, Created. + */ + FEATURE Feature; + + Feature = (FEATURE) c_alloc_struct (sizeof (FEATURE_STRUCT) + + (FeatureDesc->NumParams - 1) * + sizeof (FLOAT32), + "sizeof(FEATURE_STRUCT)+sizeof(FLOAT32)*(NumParamsIn(Feature)-1)"); + TypeOf (Feature) = FeatureDesc; + return (Feature); + +} /* NewFeature */ + + +/*---------------------------------------------------------------------------*/ +FEATURE_SET NewFeatureSet(int NumFeatures) { +/* + ** Parameters: + ** NumFeatures maximum # of features to be put in feature set + ** Globals: none + ** Operation: Allocate and return a new feature set large enough to + ** hold the specified number of features. + ** Return: New feature set. + ** Exceptions: none + ** History: Mon May 21 14:22:40 1990, DSJ, Created. + */ + FEATURE_SET FeatureSet; + + FeatureSet = (FEATURE_SET) Emalloc (sizeof (FEATURE_SET_STRUCT) + + (NumFeatures - 1) * sizeof (FEATURE)); + MaxNumFeaturesIn (FeatureSet) = NumFeatures; + NumFeaturesIn (FeatureSet) = 0; + return (FeatureSet); + +} /* NewFeatureSet */ + + +/*---------------------------------------------------------------------------*/ +FEATURE ReadFeature(FILE *File, FEATURE_DESC FeatureDesc) { +/* + ** Parameters: + ** File open text file to read feature from + ** FeatureDesc specifies type of feature to read from File + ** Globals: none + ** Operation: Create a new feature of the specified type and read in + ** the value of its parameters from File. The extra penalty + ** for the feature is also computed by calling the appropriate + ** function for the specified feature type. The correct text + ** representation for a feature is a list of N floats where + ** N is the number of parameters in the feature. + ** Return: New feature read from File. + ** Exceptions: ILLEGAL_FEATURE_PARAM if text file doesn't match expected format + ** History: Wed May 23 08:53:16 1990, DSJ, Created. + */ + FEATURE Feature; + int i; + + Feature = NewFeature (FeatureDesc); + for (i = 0; i < NumParamsIn (Feature); i++) { + if (fscanf (File, "%f", &(ParamOf (Feature, i))) != 1) + DoError (ILLEGAL_FEATURE_PARAM, "Illegal feature parameter spec"); + } + return (Feature); + +} /* ReadFeature */ + + +/*---------------------------------------------------------------------------*/ +FEATURE_SET ReadFeatureSet(FILE *File, FEATURE_DESC FeatureDesc) { +/* + ** Parameters: + ** File open text file to read new feature set from + ** FeatureDesc specifies type of feature to read from File + ** Globals: none + ** Operation: Create a new feature set of the specified type and read in + ** the features from File. The correct text representation + ** for a feature set is an integer which specifies the number (N) + ** of features in a set followed by a list of N feature + ** descriptions. + ** Return: New feature set read from File. + ** Exceptions: none + ** History: Wed May 23 09:17:31 1990, DSJ, Created. + */ + FEATURE_SET FeatureSet; + int NumFeatures; + int i; + + if (fscanf (File, "%d", &NumFeatures) != 1 || NumFeatures < 0) + DoError (ILLEGAL_NUM_FEATURES, "Illegal number of features in set"); + + FeatureSet = NewFeatureSet (NumFeatures); + for (i = 0; i < NumFeatures; i++) + AddFeature (FeatureSet, ReadFeature (File, FeatureDesc)); + + return (FeatureSet); + +} /* ReadFeatureSet */ + + +/*---------------------------------------------------------------------------*/ +void WriteFeature(FILE *File, FEATURE Feature) { +/* + ** Parameters: + ** File open text file to write Feature to + ** Feature feature to write out to File + ** Globals: none + ** Operation: Write a textual representation of Feature to File. + ** This representation is simply a list of the N parameters + ** of the feature, terminated with a newline. It is assumed + ** that the ExtraPenalty field can be reconstructed from the + ** parameters of the feature. It is also assumed that the + ** feature type information is specified or assumed elsewhere. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 09:28:18 1990, DSJ, Created. + */ + int i; + + for (i = 0; i < NumParamsIn (Feature); i++) + fprintf (File, " %12g", ParamOf (Feature, i)); + fprintf (File, "\n"); + +} /* WriteFeature */ + + +/*---------------------------------------------------------------------------*/ +void WriteFeatureSet(FILE *File, FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** File open text file to write FeatureSet to + ** FeatureSet feature set to write to File + ** Globals: none + ** Operation: Write a textual representation of FeatureSet to File. + ** This representation is an integer specifying the number of + ** features in the set, followed by a newline, followed by + ** text representations for each feature in the set. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 10:06:03 1990, DSJ, Created. + */ + int i; + + if (FeatureSet) { + fprintf (File, "%d\n", NumFeaturesIn (FeatureSet)); + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) + WriteFeature (File, FeatureIn (FeatureSet, i)); + } +} /* WriteFeatureSet */ + + +/*---------------------------------------------------------------------------*/ +void WriteOldParamDesc(FILE *File, FEATURE_DESC FeatureDesc) { +/* + ** Parameters: + ** File open text file to write FeatureDesc to + ** FeatureDesc feature descriptor to write to File + ** Globals: none + ** Operation: Write a textual representation of FeatureDesc to File + ** in the old format (i.e. the format used by the clusterer). + ** This format is: + ** Number of Params + ** Description of Param 1 + ** ... + ** Return: none + ** Exceptions: none + ** History: Fri May 25 15:27:18 1990, DSJ, Created. + */ + int i; + + fprintf (File, "%d\n", FeatureDesc->NumParams); + for (i = 0; i < FeatureDesc->NumParams; i++) { + if (FeatureDesc->ParamDesc[i].Circular) + fprintf (File, "circular "); + else + fprintf (File, "linear "); + + if (FeatureDesc->ParamDesc[i].NonEssential) + fprintf (File, "non-essential "); + else + fprintf (File, "essential "); + + fprintf (File, "%f %f\n", + FeatureDesc->ParamDesc[i].Min, FeatureDesc->ParamDesc[i].Max); + } +} /* WriteOldParamDesc */ diff --git a/classify/ocrfeatures.h b/classify/ocrfeatures.h new file mode 100644 index 0000000000..6c9e4c9a4b --- /dev/null +++ b/classify/ocrfeatures.h @@ -0,0 +1,170 @@ +/****************************************************************************** + ** Filename: features.h + ** Purpose: Generic definition of a feature. + ** Author: Dan Johnson + ** History: Sun May 20 10:28:30 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef FEATURES_H +#define FEATURES_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "funcdefs.h" +#include "tessclas.h" +#include "fxdefs.h" + +#include + +#undef Min +#undef Max +#define FEAT_NAME_SIZE 80 + +/*define trap errors which can be caused by this module*/ +#define ILLEGAL_FEATURE_PARAM 1000 +#define ILLEGAL_NUM_FEATURES 1001 + +/* A character is described by multiple sets of extracted features. Each + set contains a number of features of a particular type, for example, a + set of bays, or a set of closures, or a set of microfeatures. Each + feature consists of a number of parameters. All features within a + feature set contain the same number of parameters. All circular + parameters are required to be the first parameters in the feature.*/ + +typedef struct +{ + struct fds *Type; /* points to description of feature type */ + FLOAT32 Params[1]; /* variable size array - params for feature */ +} FEATURE_STRUCT; +typedef FEATURE_STRUCT *FEATURE; + +typedef struct +{ + UINT16 NumFeatures; /* number of features in set */ + UINT16 MaxNumFeatures; /* maximum size of feature set */ + FEATURE Features[1]; /* variable size array of features */ +} FEATURE_SET_STRUCT; +typedef FEATURE_SET_STRUCT *FEATURE_SET; + +/* Define various function types which will be needed for "class methods"*/ +typedef FEATURE (*FEAT_FUNC) (); +typedef FEATURE_SET (*FX_FUNC) (TBLOB *, LINE_STATS *); +typedef FLOAT32 (*PENALTY_FUNC) (); + +typedef struct +{ + INT8 Circular; /* TRUE if dimension wraps around */ + INT8 NonEssential; /* TRUE if dimension not used in searches */ + FLOAT32 Min; /* low end of range for circular dimensions */ + FLOAT32 Max; /* high end of range for circular dimensions */ + FLOAT32 Range; /* Max - Min */ + FLOAT32 HalfRange; /* (Max - Min)/2 */ + FLOAT32 MidRange; /* (Max + Min)/2 */ +} PARAM_DESC; + +typedef struct fds +{ + UINT16 NumParams; /* total # of params */ + UINT8 NumLinearParams; /* # of linear params */ + UINT8 NumCircularParams; /* # of linear params */ + UINT8 MinFeatPerChar; /* min # of feats allowed */ + UINT8 MaxFeatPerChar; /* max # of feats allowed */ + char LongName[FEAT_NAME_SIZE]; /* long name for feature */ + char ShortName[FEAT_NAME_SIZE];/* short name for feature */ + PARAM_DESC *ParamDesc; /* array - one per param */ +} FEATURE_DESC_STRUCT; + /* one per feature type */ +typedef FEATURE_DESC_STRUCT *FEATURE_DESC; + +typedef struct fxs +{ + FX_FUNC Extractor; /* func to extract features */ + VOID_FUNC InitExtractorVars; /* func to init fx controls */ +} FEATURE_EXT_STRUCT; + +/*---------------------------------------------------------------------- + Macros for defining the parameters of a new features +----------------------------------------------------------------------*/ +#define StartParamDesc(Name) \ +static PARAM_DESC Name[] = { + +#define DefineParam(Circular, NonEssential, Min, Max) \ + {Circular, NonEssential, Min, Max, \ + (Max) - (Min), (((Max) - (Min))/2.0), (((Max) + (Min))/2.0)}, + +#define EndParamDesc }; + +/*---------------------------------------------------------------------- +Macro for describing a new feature. The parameters of the macro +are as follows: + +DefineFeature (Name, NumLinear, NumCircular, + MinFeatPerChar, MaxFeatPerChar, + LongName, ShortName, ParamName, + Extractor, Displayer, + ComputeExtraPenalty, + InitExtractor, InitExtractorVars, TweekExtractorVars) +----------------------------------------------------------------------*/ +#define DefineFeature(Name, NL, NC, Min, Max, LN, SN, PN) \ +FEATURE_DESC_STRUCT Name = { \ + ((NL) + (NC)), NL, NC, Min, Max, LN, SN, PN}; +#define DefineFeatureExt(Name, E, IEV) FEATURE_EXT_STRUCT Name = {E, IEV}; + +/*---------------------------------------------------------------------- + Macros for accessing features +----------------------------------------------------------------------*/ +#define TypeOf(Feature) ((Feature)->Type) +#define ParamOf(Feature, N) ((Feature)->Params[N]) +#define NumParamsIn(Feature) (TypeOf (Feature) -> NumParams) + +/*---------------------------------------------------------------------- + Macros for accessing feature sets +----------------------------------------------------------------------*/ +#define NumFeaturesIn(Set) ((Set)->NumFeatures) +#define MaxNumFeaturesIn(Set) ((Set)->MaxNumFeatures) +#define FeatureIn(Set, N) ((Set)->Features[N]) + +/*---------------------------------------------------------------------- + Macros for accessing feature descriptions +----------------------------------------------------------------------*/ +#define ShortNameOf(FeatDesc) ((FeatDesc)->ShortName) +#define LongNameOf(FeatDesc) ((FeatDesc)->LongName) +#define ExtractUsing(FeatDesc) (*(FeatDesc)->Extractor) +#define InitFXVarsUsing(FD) (*(FD)->InitExtractorVars) + +/*---------------------------------------------------------------------- + Generic routines that work for all feature types +----------------------------------------------------------------------*/ +BOOL8 AddFeature(FEATURE_SET FeatureSet, FEATURE Feature); + +void DefaultInitFXVars(); + +void FreeFeature(FEATURE Feature); + +void FreeFeatureSet(FEATURE_SET FeatureSet); + +FEATURE NewFeature(FEATURE_DESC FeatureDesc); + +FEATURE_SET NewFeatureSet(int NumFeatures); + +FEATURE ReadFeature(FILE *File, FEATURE_DESC FeatureDesc); + +FEATURE_SET ReadFeatureSet(FILE *File, FEATURE_DESC FeatureDesc); + +void WriteFeature(FILE *File, FEATURE Feature); + +void WriteFeatureSet(FILE *File, FEATURE_SET FeatureSet); + +void WriteOldParamDesc(FILE *File, FEATURE_DESC FeatureDesc); +#endif diff --git a/classify/outfeat.cpp b/classify/outfeat.cpp new file mode 100644 index 0000000000..1200f306a7 --- /dev/null +++ b/classify/outfeat.cpp @@ -0,0 +1,262 @@ +/****************************************************************************** + ** Filename: outfeat.c + ** Purpose: Definition of outline-features. + ** Author: Dan Johnson + ** History: 11/13/90, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "outfeat.h" +#include "mfoutline.h" +#include "variables.h" +#include "sigmenu.h" + +#include "ocrfeatures.h" //Debug +#include //Debug +#include "efio.h" //Debug +//#include "christydbg.h" + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/outfeat.c +void AddOutlineFeatureToSet + _ARGS((FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet)); + +void ConvertToOutlineFeatures + _ARGS((MFOUTLINE Outline, + FEATURE_SET FeatureSet)); + +void NormalizeOutlineX + _ARGS((FEATURE_SET FeatureSet)); + +#undef _ARGS +*/ +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FEATURE_SET ExtractOutlineFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract pico-features from + ** LineStats statistics on text row blob is in + ** Globals: none + ** Operation: Convert each segment in the outline to a feature + ** and return the features. + ** Return: Outline-features for Blob. + ** Exceptions: none + ** History: 11/13/90, DSJ, Created. + ** 05/24/91, DSJ, Updated for either char or baseline normalize. + */ + LIST Outlines; + LIST RemainingOutlines; + MFOUTLINE Outline; + FEATURE_SET FeatureSet; + FLOAT32 XScale, YScale; + + FeatureSet = NewFeatureSet (MAX_OUTLINE_FEATURES); + if (Blob == NULL) + return (FeatureSet); + + Outlines = ConvertBlob (Blob); + + NormalizeOutlines(Outlines, LineStats, &XScale, &YScale); + RemainingOutlines = Outlines; + iterate(RemainingOutlines) { + Outline = (MFOUTLINE) first (RemainingOutlines); + /*---------Debug--------------------------------------------------* + OFile = fopen ("f:/ims/debug/ofOutline.logCPP", "r"); + if (OFile == NULL) + { + OFile = Efopen ("f:/ims/debug/ofOutline.logCPP", "w"); + WriteOutline(OFile, Outline); + } + else + { + fclose (OFile); + OFile = Efopen ("f:/ims/debug/ofOutline.logCPP", "a"); + } + WriteOutline(OFile, Outline); + fclose (OFile); + *--------------------------------------------------------------------*/ + ConvertToOutlineFeatures(Outline, FeatureSet); + } + if (NormMethod == baseline) + NormalizeOutlineX(FeatureSet); + /*---------Debug--------------------------------------------------* + File = fopen ("f:/ims/debug/ofFeatSet.logCPP", "r"); + if (File == NULL) + { + File = Efopen ("f:/ims/debug/ofFeatSet.logCPP", "w"); + WriteFeatureSet(File, FeatureSet); + } + else + { + fclose (File); + File = Efopen ("f:/ims/debug/ofFeatSet.logCPP", "a"); + } + WriteFeatureSet(File, FeatureSet); + fclose (File); + *--------------------------------------------------------------------*/ + FreeOutlines(Outlines); + return (FeatureSet); +} /* ExtractOutlineFeatures */ + + +/*---------------------------------------------------------------------------*/ +void InitOutlineFXVars() { + //once contained a dummy +/* + ** Parameters: none + ** Globals: none + ** Operation: Initialize the outline-feature extractor variables that can + ** be tuned without recompiling. + ** Return: none + ** Exceptions: none + ** History: 11/13/90, DSJ, Created. + */ +} /* InitOutlineFXVars */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void AddOutlineFeatureToSet(FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** Start starting point of outline-feature + ** End ending point of outline-feature + ** FeatureSet set to add outline-feature to + ** Globals: none + ** Operation: This routine computes the midpoint between Start and + ** End to obtain the x,y position of the outline-feature. It + ** also computes the direction from Start to End as the + ** direction of the outline-feature and the distance from + ** Start to End as the length of the outline-feature. + ** This feature is then + ** inserted into the next feature slot in FeatureSet. + ** Return: none (results are placed in FeatureSet) + ** Exceptions: none + ** History: 11/13/90, DSJ, Created. + */ + FEATURE Feature; + + Feature = NewFeature (&OutlineFeatDesc); + ParamOf (Feature, OutlineFeatDir) = NormalizedAngleFrom (Start, End, 1.0); + ParamOf (Feature, OutlineFeatX) = AverageOf (Xof (*Start), Xof (*End)); + ParamOf (Feature, OutlineFeatY) = AverageOf (Yof (*Start), Yof (*End)); + ParamOf (Feature, OutlineFeatLength) = DistanceBetween (*Start, *End); + AddFeature(FeatureSet, Feature); + +} /* AddOutlineFeatureToSet */ + + +/*---------------------------------------------------------------------------*/ +void ConvertToOutlineFeatures(MFOUTLINE Outline, FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** Outline outline to extract outline-features from + ** FeatureSet set of features to add outline-features to + ** Globals: none + ** Operation: + ** This routine steps converts each section in the specified + ** outline to a feature described by its x,y position, length + ** and angle. + ** Return: none (results are returned in FeatureSet) + ** Exceptions: none + ** History: 11/13/90, DSJ, Created. + ** 5/24/91, DSJ, Added hidden edge capability. + */ + MFOUTLINE Next; + MFOUTLINE First; + FPOINT FeatureStart; + FPOINT FeatureEnd; + + if (DegenerateOutline (Outline)) + return; + + First = Outline; + Next = First; + do { + CopyPoint (PositionOf (PointAt (Next)), FeatureStart); + Next = NextPointAfter (Next); + + /* note that an edge is hidden if the ending point of the edge is + marked as hidden. This situation happens because the order of + the outlines is reversed when they are converted from the old + format. In the old format, a hidden edge is marked by the + starting point for that edge. */ + if (IsVisible (PointAt (Next))) { + CopyPoint (PositionOf (PointAt (Next)), FeatureEnd); + AddOutlineFeatureToSet(&FeatureStart, &FeatureEnd, FeatureSet); + } + } + while (Next != First); +} /* ConvertToOutlineFeatures */ + + +/*---------------------------------------------------------------------------*/ +void NormalizeOutlineX(FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** FeatureSet outline-features to be normalized + ** Globals: none + ** Operation: This routine computes the weighted average x position + ** over all of the outline-features in FeatureSet and then + ** renormalizes the outline-features to force this average + ** to be the x origin (i.e. x=0). + ** Return: none (FeatureSet is changed) + ** Exceptions: none + ** History: 11/13/90, DSJ, Created. + */ + int i; + FEATURE Feature; + FLOAT32 Length; + FLOAT32 TotalX = 0.0; + FLOAT32 TotalWeight = 0.0; + FLOAT32 Origin; + + if (NumFeaturesIn (FeatureSet) <= 0) + return; + + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) { + Feature = FeatureIn (FeatureSet, i); + Length = ParamOf (Feature, OutlineFeatLength); + TotalX += ParamOf (Feature, OutlineFeatX) * Length; + TotalWeight += Length; + } + Origin = TotalX / TotalWeight; + + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) { + Feature = FeatureIn (FeatureSet, i); + ParamOf (Feature, OutlineFeatX) -= Origin; + } +} /* NormalizeOutlineX */ diff --git a/classify/outfeat.h b/classify/outfeat.h new file mode 100644 index 0000000000..818a0c3d25 --- /dev/null +++ b/classify/outfeat.h @@ -0,0 +1,76 @@ +/****************************************************************************** + ** Filename: outfeat.h + ** Purpose: Definition of outline features. + ** Author: Dan Johnson + ** History: 11/13/90, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef OUTFEAT_H +#define OUTFEAT_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "ocrfeatures.h" +#include "fpoint.h" +#include "mfoutline.h" + +typedef enum { + OutlineFeatX, + OutlineFeatY, + OutlineFeatLength, + OutlineFeatDir +} OUTLINE_FEAT_PARAM_NAME; + +#define MAX_OUTLINE_FEATURES (100) +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +FEATURE_SET ExtractOutlineFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +void InitOutlineFXVars(); + +/*--------------------------------------------------------------------------- + Privat Function Prototypes +----------------------------------------------------------------------------*/ +void AddOutlineFeatureToSet(FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet); + +void ConvertToOutlineFeatures(MFOUTLINE Outline, FEATURE_SET FeatureSet); + +void NormalizeOutlineX(FEATURE_SET FeatureSet); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* outfeat.c * +FEATURE_SET ExtractOutlineFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats)); + +void InitOutlineFXVars + _ARGS((void)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern FEATURE_DESC_STRUCT OutlineFeatDesc; +#endif diff --git a/classify/picofeat.cpp b/classify/picofeat.cpp new file mode 100644 index 0000000000..37d22ed48b --- /dev/null +++ b/classify/picofeat.cpp @@ -0,0 +1,297 @@ +/****************************************************************************** + ** Filename: picofeat.c + ** Purpose: Definition of pico-features. + ** Author: Dan Johnson + ** History: 9/4/90, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "picofeat.h" +#include "mfoutline.h" +#include "variables.h" +#include "sigmenu.h" +#include "hideedge.h" +#include "fpoint.h" + +#include + +#include "ocrfeatures.h" //Debug +#include //Debug +#include "efio.h" //Debug +//#include "christydbg.h" + +#define PICO_FEATURE_LENGTH 0.05 + +/*--------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------*/ +void ConvertSegmentToPicoFeat(FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet); + +void ConvertToPicoFeatures2(MFOUTLINE Outline, FEATURE_SET FeatureSet); + +void NormalizePicoX(FEATURE_SET FeatureSet); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/picofeat.c +void ConvertSegmentToPicoFeat + _ARGS((FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet)); + +void ConvertToPicoFeatures2 + _ARGS((MFOUTLINE Outline, + FEATURE_SET FeatureSet)); + +void NormalizePicoX + _ARGS((FEATURE_SET FeatureSet)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FEATURE_SET ExtractPicoFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract pico-features from + ** LineStats statistics on text row blob is in + ** Globals: + ** NormMethod normalization method currently specified + ** Operation: Dummy for now. + ** Return: Pico-features for Blob. + ** Exceptions: none + ** History: 9/4/90, DSJ, Created. + */ + LIST Outlines; + LIST RemainingOutlines; + MFOUTLINE Outline; + FEATURE_SET FeatureSet; + FLOAT32 XScale, YScale; + + FeatureSet = NewFeatureSet (MAX_PICO_FEATURES); + + Outlines = ConvertBlob (Blob); + + NormalizeOutlines(Outlines, LineStats, &XScale, &YScale); + RemainingOutlines = Outlines; + iterate(RemainingOutlines) { + Outline = (MFOUTLINE) first (RemainingOutlines); + /*---------Debug--------------------------------------------------* + OFile = fopen ("f:/ims/debug/pfOutline.logCPP", "r"); + if (OFile == NULL) + { + OFile = Efopen ("f:/ims/debug/pfOutline.logCPP", "w"); + WriteOutline(OFile, Outline); + } + else + { + fclose (OFile); + OFile = Efopen ("f:/ims/debug/pfOutline.logCPP", "a"); + } + WriteOutline(OFile, Outline); + fclose (OFile); + *--------------------------------------------------------------------*/ + ConvertToPicoFeatures2(Outline, FeatureSet); + } + if (NormMethod == baseline) + NormalizePicoX(FeatureSet); + /*---------Debug--------------------------------------------------* + File = fopen ("f:/ims/debug/pfFeatSet.logCPP", "r"); + if (File == NULL) + { + File = Efopen ("f:/ims/debug/pfFeatSet.logCPP", "w"); + WriteFeatureSet(File, FeatureSet); + } + else + { + fclose (File); + File = Efopen ("f:/ims/debug/pfFeatSet.logCPP", "a"); + } + WriteFeatureSet(File, FeatureSet); + fclose (File); + *--------------------------------------------------------------------*/ + FreeOutlines(Outlines); + return (FeatureSet); + +} /* ExtractPicoFeatures */ + + +/*---------------------------------------------------------------------------*/ +void InitPicoFXVars() { +/* + ** Parameters: none + ** Globals: + ** PicoFeatureLength controls length of pico-features + ** Operation: Initialize the pico-feature extractor variables that can + ** be tuned without recompiling. + ** Return: none + ** Exceptions: none + ** History: 9/4/90, DSJ, Created. + */ + + VALUE dummy; + + float_variable (PicoFeatureLength, "PicoFeatureLength", + PICO_FEATURE_LENGTH); + +} /* InitPicoFXVars */ + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ConvertSegmentToPicoFeat(FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** Start starting point of pico-feature + ** End ending point of pico-feature + ** FeatureSet set to add pico-feature to + ** Globals: + ** PicoFeatureLength length of a single pico-feature + ** Operation: This routine converts an entire segment of an outline + ** into a set of pico features which are added to + ** FeatureSet. The length of the segment is rounded to the + ** nearest whole number of pico-features. The pico-features + ** are spaced evenly over the entire segment. + ** Return: none (results are placed in FeatureSet) + ** Exceptions: none + ** History: Tue Apr 30 15:44:34 1991, DSJ, Created. + */ + FEATURE Feature; + FLOAT32 Angle; + FLOAT32 Length; + int NumFeatures; + FPOINT Center; + FPOINT Delta; + int i; + + Angle = NormalizedAngleFrom (Start, End, 1.0); + Length = DistanceBetween (*Start, *End); + NumFeatures = (int) floor (Length / PicoFeatureLength + 0.5); + if (NumFeatures < 1) + NumFeatures = 1; + + /* compute vector for one pico feature */ + Xof (Delta) = XDelta (*Start, *End) / NumFeatures; + Yof (Delta) = YDelta (*Start, *End) / NumFeatures; + + /* compute position of first pico feature */ + Xof (Center) = Xof (*Start) + Xof (Delta) / 2.0; + Yof (Center) = Yof (*Start) + Yof (Delta) / 2.0; + + /* compute each pico feature in segment and add to feature set */ + for (i = 0; i < NumFeatures; i++) { + Feature = NewFeature (&PicoFeatDesc); + ParamOf (Feature, PicoFeatDir) = Angle; + ParamOf (Feature, PicoFeatX) = Xof (Center); + ParamOf (Feature, PicoFeatY) = Yof (Center); + AddFeature(FeatureSet, Feature); + + Xof (Center) += Xof (Delta); + Yof (Center) += Yof (Delta); + } +} /* ConvertSegmentToPicoFeat */ + + +/*---------------------------------------------------------------------------*/ +void ConvertToPicoFeatures2(MFOUTLINE Outline, FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** Outline outline to extract micro-features from + ** FeatureSet set of features to add pico-features to + ** Globals: + ** PicoFeatureLength length of features to be extracted + ** Operation: + ** This routine steps thru the specified outline and cuts it + ** up into pieces of equal length. These pieces become the + ** desired pico-features. Each segment in the outline + ** is converted into an integral number of pico-features. + ** Return: none (results are returned in FeatureSet) + ** Exceptions: none + ** History: 4/30/91, DSJ, Adapted from ConvertToPicoFeatures(). + */ + MFOUTLINE Next; + MFOUTLINE First; + MFOUTLINE Current; + + if (DegenerateOutline (Outline)) + return; + + First = Outline; + Current = First; + Next = NextPointAfter (Current); + do { + /* note that an edge is hidden if the ending point of the edge is + marked as hidden. This situation happens because the order of + the outlines is reversed when they are converted from the old + format. In the old format, a hidden edge is marked by the + starting point for that edge. */ + if (IsVisible (PointAt (Next))) + ConvertSegmentToPicoFeat (&(PositionOf (PointAt (Current))), + &(PositionOf (PointAt (Next))), FeatureSet); + + Current = Next; + Next = NextPointAfter (Current); + } + while (Current != First); + +} /* ConvertToPicoFeatures2 */ + + +/*---------------------------------------------------------------------------*/ +void NormalizePicoX(FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** FeatureSet pico-features to be normalized + ** Globals: none + ** Operation: This routine computes the average x position over all + ** of the pico-features in FeatureSet and then renormalizes + ** the pico-features to force this average to be the x origin + ** (i.e. x=0). + ** Return: none (FeatureSet is changed) + ** Exceptions: none + ** History: Tue Sep 4 16:50:08 1990, DSJ, Created. + */ + int i; + FEATURE Feature; + FLOAT32 Origin = 0.0; + + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) { + Feature = FeatureIn (FeatureSet, i); + Origin += ParamOf (Feature, PicoFeatX); + } + Origin /= NumFeaturesIn (FeatureSet); + + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) { + Feature = FeatureIn (FeatureSet, i); + ParamOf (Feature, PicoFeatX) -= Origin; + } +} /* NormalizePicoX */ diff --git a/classify/picofeat.h b/classify/picofeat.h new file mode 100644 index 0000000000..8c08ee6b42 --- /dev/null +++ b/classify/picofeat.h @@ -0,0 +1,65 @@ +/****************************************************************************** + ** Filename: picofeat.h + ** Purpose: Definition of pico features. + ** Author: Dan Johnson + ** History: 9/4/90, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef PICOFEAT_H +#define PICOFEAT_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "ocrfeatures.h" +#include "tessclas.h" +#include "fxdefs.h" + +typedef enum +{ PicoFeatY, PicoFeatDir, PicoFeatX } +PICO_FEAT_PARAM_NAME; + +#define MAX_PICO_FEATURES (1000) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +#define GetPicoFeatureLength() (PicoFeatureLength) + +FEATURE_SET ExtractPicoFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +void InitPicoFXVars(); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* picofeat.c +FEATURE_SET ExtractPicoFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats)); + +void InitPicoFXVars + _ARGS((void)); + +#undef _ARGS +*/ +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern FEATURE_DESC_STRUCT PicoFeatDesc; +extern FLOAT32 PicoFeatureLength; +#endif diff --git a/classify/protos.cpp b/classify/protos.cpp new file mode 100644 index 0000000000..752d84523b --- /dev/null +++ b/classify/protos.cpp @@ -0,0 +1,468 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: protos.c (Formerly protos.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon Mar 4 14:51:24 1991 (Dan Johnson) danj@hpgrlj + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "protos.h" +#include "debug.h" +#include "const.h" +#include "emalloc.h" +#include "freelist.h" +#include "callcpp.h" +#include "adaptmatch.h" +#include "scanutils.h" + +#include +#include + +#define PROTO_INCREMENT 32 +#define CONFIG_INCREMENT 16 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +CLASS_STRUCT TrainingData[NUMBER_OF_CLASSES]; + +char *TrainingFile; + +//extern int LearningDebugLevel; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * AddConfigToClass + * + * Add a new config to this class. Malloc new space and copy the + * old configs if necessary. Return the config id for the new config. + **********************************************************************/ +int AddConfigToClass(CLASS_TYPE Class) { + int NewNumConfigs; + int NewConfig; + int MaxNumProtos; + BIT_VECTOR Config; + + MaxNumProtos = Class->MaxNumProtos; + + if (NumConfigsIn (Class) >= Class->MaxNumConfigs) { + /* add configs in CONFIG_INCREMENT chunks at a time */ + NewNumConfigs = (((Class->MaxNumConfigs + CONFIG_INCREMENT) / + CONFIG_INCREMENT) * CONFIG_INCREMENT); + + Class->Configurations = + (CONFIGS) Erealloc (Class->Configurations, + sizeof (BIT_VECTOR) * NewNumConfigs); + + Class->MaxNumConfigs = NewNumConfigs; + } + NewConfig = NumConfigsIn (Class); + NumConfigsIn (Class)++; + Config = NewBitVector (MaxNumProtos); + ConfigIn (Class, NewConfig) = Config; + zero_all_bits (Config, WordsInVectorOfSize (MaxNumProtos)); + + return (NewConfig); +} + + +/********************************************************************** + * AddProtoToClass + * + * Add a new proto to this class. Malloc new space and copy the + * old protos if necessary. Return the proto id for the new proto. + **********************************************************************/ +int AddProtoToClass(CLASS_TYPE Class) { + int i; + int Bit; + int NewNumProtos; + int NewProto; + BIT_VECTOR Config; + + if (NumProtosIn (Class) >= Class->MaxNumProtos) { + /* add protos in PROTO_INCREMENT chunks at a time */ + NewNumProtos = (((Class->MaxNumProtos + PROTO_INCREMENT) / + PROTO_INCREMENT) * PROTO_INCREMENT); + + Class->Prototypes = (PROTO) Erealloc (Class->Prototypes, + sizeof (PROTO_STRUCT) * + NewNumProtos); + + Class->MaxNumProtos = NewNumProtos; + + for (i = 0; i < NumConfigsIn (Class); i++) { + Config = ConfigIn (Class, i); + ConfigIn (Class, i) = ExpandBitVector (Config, NewNumProtos); + + for (Bit = NumProtosIn (Class); Bit < NewNumProtos; Bit++) + reset_bit(Config, Bit); + } + } + NewProto = NumProtosIn (Class); + NumProtosIn (Class)++; + return (NewProto); +} + + +/********************************************************************** + * ClassConfigLength + * + * Return the length of all the protos in this class. + **********************************************************************/ +FLOAT32 ClassConfigLength(CLASS_TYPE Class, BIT_VECTOR Config) { + INT16 Pid; + FLOAT32 TotalLength = 0; + + for (Pid = 0; Pid < NumProtosIn (Class); Pid++) { + if (test_bit (Config, Pid)) { + + TotalLength += ProtoLength (ProtoIn (Class, Pid)); + } + } + return (TotalLength); +} + + +/********************************************************************** + * ClassProtoLength + * + * Return the length of all the protos in this class. + **********************************************************************/ +FLOAT32 ClassProtoLength(CLASS_TYPE Class) { + INT16 Pid; + FLOAT32 TotalLength = 0; + + for (Pid = 0; Pid < NumProtosIn (Class); Pid++) { + TotalLength += ProtoLength (ProtoIn (Class, Pid)); + } + return (TotalLength); +} + + +/********************************************************************** + * CopyProto + * + * Copy the first proto into the second. + **********************************************************************/ +void CopyProto(PROTO Src, PROTO Dest) { + ProtoX (Dest) = ProtoX (Src); + ProtoY (Dest) = ProtoY (Src); + ProtoLength (Dest) = ProtoLength (Src); + ProtoAngle (Dest) = ProtoAngle (Src); + CoefficientA (Dest) = CoefficientA (Src); + CoefficientB (Dest) = CoefficientB (Src); + CoefficientC (Dest) = CoefficientC (Src); +} + + +/********************************************************************** + * FillABC + * + * Fill in Protos A, B, C fields based on the X, Y, Angle fields. + **********************************************************************/ +void FillABC(PROTO Proto) { + FLOAT32 Slope, Intercept, Normalizer; + + Slope = tan (Proto->Angle * 2.0 * PI); + Intercept = Proto->Y - Slope * Proto->X; + Normalizer = 1.0 / sqrt (Slope * Slope + 1.0); + Proto->A = Slope * Normalizer; + Proto->B = -Normalizer; + Proto->C = Intercept * Normalizer; +} + + +/********************************************************************** + * FreeClass + * + * Deallocate the memory consumed by the specified class. + **********************************************************************/ +void FreeClass(CLASS_TYPE Class) { + if (Class) { + FreeClassFields(Class); + memfree(Class); + } +} + + +/********************************************************************** + * FreeClassFields + * + * Deallocate the memory consumed by subfields of the specified class. + **********************************************************************/ +void FreeClassFields(CLASS_TYPE Class) { + int i; + + if (Class) { + if (Class->MaxNumProtos > 0) + memfree (Class->Prototypes); + if (Class->MaxNumConfigs > 0) { + for (i = 0; i < NumConfigsIn (Class); i++) + FreeBitVector (ConfigIn (Class, i)); + memfree (Class->Configurations); + } + } +} + + +/********************************************************************** + * InitPrototypes + * + * Initialize anything that needs to be initialized to work with the + * functions in this file. + **********************************************************************/ +void InitPrototypes() { + string_variable (TrainingFile, "TrainingFile", "MicroFeatures"); +} + + +/********************************************************************** + * NewClass + * + * Allocate a new class with enough memory to hold the specified number + * of prototypes and configurations. + **********************************************************************/ +CLASS_TYPE NewClass(int NumProtos, int NumConfigs) { + CLASS_TYPE Class; + + Class = (CLASS_TYPE) Emalloc (sizeof (CLASS_STRUCT)); + + if (NumProtos > 0) + Class->Prototypes = (PROTO) Emalloc (NumProtos * sizeof (PROTO_STRUCT)); + + if (NumConfigs > 0) + Class->Configurations = (CONFIGS) Emalloc (NumConfigs * + sizeof (BIT_VECTOR)); + Class->MaxNumProtos = NumProtos; + Class->MaxNumConfigs = NumConfigs; + Class->NumProtos = 0; + Class->NumConfigs = 0; + return (Class); + +} + + +/********************************************************************** + * PrintProtos + * + * Print the list of prototypes in this class type. + **********************************************************************/ +void PrintProtos(CLASS_TYPE Class) { + INT16 Pid; + + for (Pid = 0; Pid < NumProtosIn (Class); Pid++) { + cprintf ("Proto %d:\t", Pid); + PrintProto (ProtoIn (Class, Pid)); + cprintf ("\t"); + PrintProtoLine (ProtoIn (Class, Pid)); + new_line(); + } +} + + +/********************************************************************** + * ReadClassFile + * + * Read in the training data from a file. All of the classes are read + * in. The results are stored in the global variable, 'TrainingData'. + **********************************************************************/ +void ReadClassFile() { + FILE *File; + char TextLine[CHARS_PER_LINE]; + + cprintf ("Reading training data from '%s' ...", TrainingFile); + fflush(stdout); + + File = open_file (TrainingFile, "r"); + while (fgets (TextLine, CHARS_PER_LINE, File) != NULL) { + + ReadClassFromFile (File, TextLine[0]); + fgets(TextLine, CHARS_PER_LINE, File); + fgets(TextLine, CHARS_PER_LINE, File); + } + fclose(File); + + new_line(); +} + + +/********************************************************************** + * ReadClassFromFile + * + * Read in a class description (protos and configs) from a file. Update + * the class structure record. + **********************************************************************/ +void ReadClassFromFile(FILE *File, char ClassChar) { + CLASS_TYPE Class; + + Class = &TrainingData[ClassChar]; + + ReadProtos(File, Class); + + ReadConfigs(File, Class); +} + + +/********************************************************************** + * ReadConfigs + * + * Read the prototype configurations for this class from a file. Read + * the requested number of lines. + **********************************************************************/ +void ReadConfigs(register FILE *File, CLASS_TYPE Class) { + INT16 Cid; + register INT16 Wid; + register BIT_VECTOR ThisConfig; + int NumWords; + int NumConfigs; + + fscanf (File, "%d %d\n", &NumConfigs, &NumWords); + NumConfigsIn (Class) = NumConfigs; + Class->MaxNumConfigs = NumConfigs; + Class->Configurations = + (CONFIGS) Emalloc (sizeof (BIT_VECTOR) * NumConfigs); + NumWords = WordsInVectorOfSize (NumProtosIn (Class)); + + for (Cid = 0; Cid < NumConfigs; Cid++) { + + ThisConfig = NewBitVector (NumProtosIn (Class)); + for (Wid = 0; Wid < NumWords; Wid++) + fscanf (File, "%x", &ThisConfig[Wid]); + ConfigIn (Class, Cid) = ThisConfig; + } +} + + +/********************************************************************** + * ReadProtos + * + * Read in all the prototype information from a file. Read the number + * of lines requested. + **********************************************************************/ +void ReadProtos(register FILE *File, CLASS_TYPE Class) { + register INT16 Pid; + register PROTO Proto; + int NumProtos; + + fscanf (File, "%d\n", &NumProtos); + NumProtosIn (Class) = NumProtos; + Class->MaxNumProtos = NumProtos; + Class->Prototypes = (PROTO) Emalloc (sizeof (PROTO_STRUCT) * NumProtos); + + for (Pid = 0; Pid < NumProtos; Pid++) { + Proto = ProtoIn (Class, Pid); + fscanf (File, "%f %f %f %f %f %f %f\n", + &ProtoX (Proto), + &ProtoY (Proto), + &ProtoLength (Proto), + &ProtoAngle (Proto), + &CoefficientA (Proto), + &CoefficientB (Proto), &CoefficientC (Proto)); + } +} + + +/********************************************************************** + * SplitProto + * + * Add a new proto to this class. Malloc new space and copy the + * old protos if necessary. Return the proto id for the new proto. + * Update all configurations so that each config which contained the + * specified old proto will also contain the new proto. The caller + * is responsible for actually filling in the appropriate proto params. + **********************************************************************/ +int SplitProto(CLASS_TYPE Class, int OldPid) { + int i; + int NewPid; + BIT_VECTOR Config; + + NewPid = AddProtoToClass (Class); + + for (i = 0; i < NumConfigsIn (Class); i++) { + Config = ConfigIn (Class, i); + if (test_bit (Config, OldPid)) + SET_BIT(Config, NewPid); + } + return (NewPid); +} + + +/********************************************************************** + * WriteOldConfigFile + * + * Write the configs in the given class to the specified file in the + * old config format. + **********************************************************************/ +void WriteOldConfigFile(FILE *File, CLASS_TYPE Class) { + int Cid, Pid; + BIT_VECTOR Config; + + fprintf (File, "%d %d\n", NumConfigsIn (Class), NumProtosIn (Class)); + + for (Cid = 0; Cid < NumConfigsIn (Class); Cid++) { + fprintf (File, "1 "); + + Config = ConfigIn (Class, Cid); + + for (Pid = 0; Pid < NumProtosIn (Class); Pid++) { + if (test_bit (Config, Pid)) + fprintf (File, "1"); + else + fprintf (File, "0"); + } + fprintf (File, "\n"); + } +} + + +/********************************************************************** + * WriteOldProtoFile + * + * Write the protos in the given class to the specified file in the + * old proto format. + **********************************************************************/ +void WriteOldProtoFile(FILE *File, CLASS_TYPE Class) { + int Pid; + PROTO Proto; + + /* print old header */ + fprintf (File, "6\n"); + fprintf (File, "linear essential -0.500000 0.500000\n"); + fprintf (File, "linear essential -0.250000 0.750000\n"); + fprintf (File, "linear essential 0.000000 1.000000\n"); + fprintf (File, "circular essential 0.000000 1.000000\n"); + fprintf (File, "linear non-essential -0.500000 0.500000\n"); + fprintf (File, "linear non-essential -0.500000 0.500000\n"); + + for (Pid = 0; Pid < NumProtosIn (Class); Pid++) { + Proto = ProtoIn (Class, Pid); + + fprintf (File, "significant elliptical 1\n"); + fprintf (File, " %9.6f %9.6f %9.6f %9.6f %9.6f %9.6f\n", + ProtoX (Proto), ProtoY (Proto), + ProtoLength (Proto), ProtoAngle (Proto), 0.0, 0.0); + fprintf (File, " %9.6f %9.6f %9.6f %9.6f %9.6f %9.6f\n", + 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001); + } +} diff --git a/classify/protos.h b/classify/protos.h new file mode 100644 index 0000000000..b669cd7fbc --- /dev/null +++ b/classify/protos.h @@ -0,0 +1,360 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: protos.h (Formerly protos.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Fri Jul 12 10:06:55 1991 (Dan Johnson) danj@hpgrlj + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef PROTOS_H +#define PROTOS_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "bitvec.h" +#include "cutil.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef BIT_VECTOR *CONFIGS; + +typedef struct +{ + FLOAT32 A; + FLOAT32 B; + FLOAT32 C; + FLOAT32 X; + FLOAT32 Y; + FLOAT32 Angle; + FLOAT32 Length; +} PROTO_STRUCT; +typedef PROTO_STRUCT *PROTO; + +typedef struct +{ + INT16 NumProtos; + INT16 MaxNumProtos; + PROTO Prototypes; + INT16 NumConfigs; + INT16 MaxNumConfigs; + CONFIGS Configurations; +} CLASS_STRUCT; +typedef CLASS_STRUCT *CLASS_TYPE; +typedef CLASS_STRUCT *CLASSES; + +/*---------------------------------------------------------------------- + C o n s t a n t s +----------------------------------------------------------------------*/ +#define NUMBER_OF_CLASSES 256 +#define Y_OFFSET -40.0 +#define FEATURE_SCALE 100.0 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern CLASS_STRUCT TrainingData[]; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * AddProtoToConfig + * + * Set a single proto bit in the specified configuration. + **********************************************************************/ + +#define AddProtoToConfig(Pid,Config) \ +(SET_BIT (Config, Pid)) + +/********************************************************************** + * RemoveProtoFromConfig + * + * Clear a single proto bit in the specified configuration. + **********************************************************************/ + +#define RemoveProtoFromConfig(Pid,Config) \ +(reset_bit (Config, Pid)) + +/********************************************************************** + * ClassOfChar + * + * Return the class of a particular ASCII character value. + **********************************************************************/ + +#define ClassOfChar(Char) \ +((TrainingData [Char].NumProtos) ? \ + (& TrainingData [Char]) : \ + NO_CLASS) + +/********************************************************************** + * ProtoIn + * + * Choose the selected prototype in this class record. Return the + * pointer to it (type PROTO). + **********************************************************************/ + +#define ProtoIn(Class,Pid) \ +(& (Class)->Prototypes [Pid]) + +/********************************************************************** + * ConfigIn + * + * Choose the selected prototype configuration in this class record. + * Return it as type 'BIT_VECTOR'. + **********************************************************************/ + +#define ConfigIn(Class,Cid) \ +((Class)->Configurations [Cid]) + +/********************************************************************** + * NumProtosIn + * + * Return the number of prototypes in this class. The 'Class' argument + * is of type 'CLASS_TYPE'. + **********************************************************************/ + +#define NumProtosIn(Class) \ +((Class)->NumProtos) + +/********************************************************************** + * NumConfigsIn + * + * Return the number of configurations in this class. The 'Class' argument + * is of type 'CLASS_TYPE'. + **********************************************************************/ + +#define NumConfigsIn(Class) \ +((Class)->NumConfigs) + +/********************************************************************** + * CoefficientA + * + * Return the first parameter of the prototype structure. This is the + * A coefficient in the line representation of "Ax + By + C = 0". The + * 'Proto' argument is of type 'PROTO'. + **********************************************************************/ + +#define CoefficientA(Proto) \ +((Proto)->A) + +/********************************************************************** + * CoefficientB + * + * Return the second parameter of the prototype structure. This is the + * B coefficient in the line representation of "Ax + By + C = 0". The + * 'Proto' argument is of type 'PROTO'. + **********************************************************************/ + +#define CoefficientB(Proto) \ +((Proto)->B) + +/********************************************************************** + * CoefficientC + * + * Return the third parameter of the prototype structure. This is the + * C coefficient in the line representation of "Ax + By + C = 0". The + * 'Proto' argument is of type 'PROTO'. + **********************************************************************/ + +#define CoefficientC(Proto) \ +((Proto)->C) + +/********************************************************************** + * ProtoAngle + * + * Return the angle parameter of the prototype structure. The + * 'Proto' argument is of type 'PROTO'. + **********************************************************************/ + +#define ProtoAngle(Proto) \ +((Proto)->Angle) + +/********************************************************************** + * ProtoX + * + * Return the X parameter of the prototype structure. The 'Proto' + * argument is of type 'PROTO'. + **********************************************************************/ + +#define ProtoX(Proto) \ +((Proto)->X) + +/********************************************************************** + * ProtoY + * + * Return the angle parameter of the prototype structure. The 'Proto' + * argument is of type 'PROTO'. + **********************************************************************/ + +#define ProtoY(Proto) \ +((Proto)->Y) + +/********************************************************************** + * ProtoLength + * + * Return the length parameter of the prototype structure. The + * 'Proto' argument is of type 'PROTO'. + **********************************************************************/ + +#define ProtoLength(Proto) \ +((Proto)->Length) + +/********************************************************************** + * PrintProto + * + * Print out the contents of a prototype. The 'Proto' argument is of + * type 'PROTO'. + **********************************************************************/ + +#define PrintProto(Proto) \ +(cprintf ("X=%4.2f, Y=%4.2f, Angle=%4.2f", \ + ProtoX (Proto), \ + ProtoY (Proto), \ + ProtoLength (Proto), \ + ProtoAngle (Proto))) \ + + +/********************************************************************** + * PrintProtoLine + * + * Print out the contents of a prototype. The 'Proto' argument is of + * type 'PROTO'. + **********************************************************************/ + +#define PrintProtoLine(Proto) \ +(cprintf ("A=%4.2f, B=%4.2f, C=%4.2f", \ + CoefficientA (Proto), \ + CoefficientB (Proto), \ + CoefficientC (Proto))) \ + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +int AddConfigToClass(CLASS_TYPE Class); + +int AddProtoToClass(CLASS_TYPE Class); + +FLOAT32 ClassConfigLength(CLASS_TYPE Class, BIT_VECTOR Config); + +FLOAT32 ClassProtoLength(CLASS_TYPE Class); + +void CopyProto(PROTO Src, PROTO Dest); + +void FillABC(PROTO Proto); + +void FreeClass(CLASS_TYPE Class); + +void FreeClassFields(CLASS_TYPE Class); + +void InitPrototypes(); + +CLASS_TYPE NewClass(int NumProtos, int NumConfigs); + +void PrintProtos(CLASS_TYPE Class); + +void ReadClassFile(); + +void ReadClassFromFile(FILE *File, char ClassChar); + +void ReadConfigs(register FILE *File, CLASS_TYPE Class); + +void ReadProtos(register FILE *File, CLASS_TYPE Class); + +int SplitProto(CLASS_TYPE Class, int OldPid); + +void WriteOldConfigFile(FILE *File, CLASS_TYPE Class); + +void WriteOldProtoFile(FILE *File, CLASS_TYPE Class); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* protos.c * +int AddConfigToClass + _ARGS((CLASS_TYPE Class)); + +int AddProtoToClass + _ARGS((CLASS_TYPE Class)); + +FLOAT32 ClassConfigLength + _ARGS((CLASS_TYPE Class, + BIT_VECTOR Config)); + +FLOAT32 ClassProtoLength + _ARGS((CLASS_TYPE Class)); + +void CopyProto + _ARGS((PROTO Src, + PROTO Dest)); + +void FillABC + _ARGS((PROTO Proto)); + +void FreeClass + _ARGS((CLASS_TYPE Class)); + +void FreeClassFields + _ARGS((CLASS_TYPE Class)); + +void InitPrototypes + _ARGS((void)); + +CLASS_TYPE NewClass + _ARGS((int NumProtos, + int NumConfigs)); + +void PrintProtos + _ARGS((CLASS_TYPE Class)); + +void ReadClassFile + _ARGS((void)); + +void ReadClassFromFile + _ARGS((FILE *File, + int ClassChar)); + +void ReadConfigs + _ARGS((FILE *File, + CLASS_TYPE Class)); + +void ReadProtos + _ARGS((FILE *File, + CLASS_TYPE Class)); + +int SplitProto + _ARGS((CLASS_TYPE Class, + int OldPid)); + +void WriteOldConfigFile + _ARGS((FILE *File, + CLASS_TYPE Class)); + +void WriteOldProtoFile + _ARGS((FILE *File, + CLASS_TYPE Class)); + +#undef _ARGS +*/ +#endif diff --git a/classify/sigmenu.cpp b/classify/sigmenu.cpp new file mode 100644 index 0000000000..783b104d96 --- /dev/null +++ b/classify/sigmenu.cpp @@ -0,0 +1,225 @@ +/****************************************************************************** + ** Filename: sigmenu.c + ** Purpose: General purpose, menu-oriented signal handling routines + ** Author: Dan Johnson + ** History: Mon Oct 2 07:25:50 1989, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "sigmenu.h" +#include "oldlist.h" +#include "emalloc.h" +#include "secname.h" + +#include +#include +#include + +#define MAX_COMMAND_LENGTH 128 + +typedef struct +{ + int ItemNum; + char *ItemLabel; + int_void ItemFunc; +} SIG_MENU_ITEM; + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +static LIST SignalMenus[NSIG]; /* must be initialized to NIL */ + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +void MainSignalHandler(int Signal); + +SIG_MENU_ITEM *NewSignalMenuItem (int ItemNum, +const char ItemLabel[], int_void ItemFunc); + +int ItemCompare(void *arg1, //SIG_MENU_ITEM *Item1, + void *arg2); //SIG_MENU_ITEM *Item2); + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void +AddSignalMenuItem (int Signal, +int ItemNum, const char ItemLabel[], int_void ItemFunc) { +/* + ** Parameters: + ** Signal signal to be trapped for this menu + ** ItemNum menu number for this item + ** ItemLabel menu label for this item + ** ItemFunc function to be called when item is selected + ** Globals: + ** SignalMenus list of menu items for each possible signal + ** Operation: + ** Add a new menu item to the list of menu items for Signal. + ** Whenever Signal is encountered, the user will be given + ** a list of options to choose from. This list is the list + ** of all of the menu items that have been specified for that + ** Signal. + ** Return: none + ** Exceptions: none + ** History: Mon Oct 2 07:42:19 1989, DSJ, Created. + */ + #if 0 + #ifndef SECURE_NAMES + SIG_MENU_ITEM *NewItem; + + /* check for a valid Signal */ + if (Signal >= NSIG || Signal <= 0) { + cprintf ("Illegal signal (%d) specified for menu item!\n", Signal); + return; + } + + /* if this is the first item for this signal, indicate that the + appropriate signal handler has been enabled */ + if (SignalMenus[Signal] == NIL) + cprintf ("Signal handler enabled for signal %d.\n", Signal); + + /* add the new menu item to the appropriate list of menu items */ + NewItem = NewSignalMenuItem (ItemNum, ItemLabel, ItemFunc); + SignalMenus[Signal] = s_adjoin (SignalMenus[Signal], NewItem, ItemCompare); + + /* set up the trap for the appropriate signal */ + signal(Signal, MainSignalHandler); + #endif + #endif +} /* AddSignalMenuItem */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void MainSignalHandler(int Signal) { +/* + ** Parameters: + ** Signal signal that caused this function to be called + ** Globals: + ** SignalMenus list of menu items for each possible signal + ** Operation: Provide the user with a menu of actions for the trapped + ** signal. Execute the appropriate function. If the function + ** returns SIG_RESUME, then terminate the signal handler and + ** resume normal processing. If the function does not return + ** SIG_RESUME, remain in the main signal handler menu. + ** Return: none + ** Exceptions: none + ** History: Mon Oct 2 08:18:52 1989, DSJ, Created. + */ + #ifndef SECURE_NAMES + int Command; + char CommandLine[MAX_COMMAND_LENGTH]; + char *Params; + LIST Items; + SIG_MENU_ITEM *MenuItem; + + while (TRUE) { + Command = -1; + cprintf ("\nMAIN SIGNAL HANDLER FOR SIGNAL %d\n", Signal); + cprintf ("0. Resume normal operation\n"); + + Items = SignalMenus[Signal]; + iterate(Items) { + MenuItem = (SIG_MENU_ITEM *) first (Items); + cprintf ("%d. %s\n", MenuItem->ItemNum, MenuItem->ItemLabel); + } + cprintf ("\nEnter Selection: "); + + while (fgets (CommandLine, MAX_COMMAND_LENGTH, stdin) == NULL + || strlen (CommandLine) <= 0); + + Command = strtol (CommandLine, &Params, 10); + if (CommandLine == Params) { + cprintf ("\nIllegal command! - Try again.\n"); + continue; + } + + if (Command == 0) + signal(Signal, MainSignalHandler); + + Items = SignalMenus[Signal]; + iterate(Items) { + MenuItem = (SIG_MENU_ITEM *) first (Items); + if (Command == MenuItem->ItemNum) { + if ((*MenuItem->ItemFunc) ( /*Params */ ) == SIG_RESUME) + signal(Signal, MainSignalHandler); + break; + } + } + if (Items == NIL) + cprintf ("\nIllegal command! - Try again.\n"); + } + #endif +} /* MainSignalHandler */ + + +/*---------------------------------------------------------------------------*/ +SIG_MENU_ITEM * +NewSignalMenuItem (int ItemNum, const char ItemLabel[], int_void ItemFunc) { +/* + ** Parameters: + ** ItemNum menu number for this item + ** ItemLabel menu label for this item + ** ItemFunc function to be called when item is selected + ** Globals: none + ** Operation: Allocate, initialize, and return a new signal menu item. + ** Return: Ptr to new signal menu item data structure. + ** Exceptions: none + ** History: Mon Oct 2 08:04:20 1989, DSJ, Created. + */ + SIG_MENU_ITEM *NewItem; + + NewItem = (SIG_MENU_ITEM *) Emalloc (sizeof (SIG_MENU_ITEM)); + NewItem->ItemNum = ItemNum; + NewItem->ItemFunc = ItemFunc; + NewItem->ItemLabel = (char *) Emalloc (strlen (ItemLabel) + 1); + strcpy (NewItem->ItemLabel, ItemLabel); + return (NewItem); + +} /* NewSignalMenuItem */ + + +/*---------------------------------------------------------------------------*/ +int ItemCompare(void *arg1, //SIG_MENU_ITEM *Item1, + void *arg2) { //SIG_MENU_ITEM *Item2) +/* + ** Parameters: + ** Item1, Item2 two menu items to be compared + ** Globals: none + ** Operation: Return -1 if the ItemNum of Item1 is less than the + ** ItemNum of Item2. Return 0 if they are equal. Return +1 + ** if the ItemNum of Item1 is greater than the ItemNum of + ** Item2. This routine is used by the list sorter to sort + ** lists of menu items according to their item number. + ** Return: -1, 0, or 1 + ** Exceptions: none + ** History: Mon Oct 2 08:11:59 1989, DSJ, Created. + */ + SIG_MENU_ITEM *Item1 = (SIG_MENU_ITEM *) arg1; + SIG_MENU_ITEM *Item2 = (SIG_MENU_ITEM *) arg2; + + if (Item1->ItemNum < Item2->ItemNum) + return (-1); + else if (Item1->ItemNum == Item2->ItemNum) + return (0); + else if (Item1->ItemNum > Item2->ItemNum) + return (1); + else + return 0; +} /* ItemCompare */ diff --git a/classify/sigmenu.h b/classify/sigmenu.h new file mode 100644 index 0000000000..7eab09f404 --- /dev/null +++ b/classify/sigmenu.h @@ -0,0 +1,39 @@ +/****************************************************************************** + ** Filename: sigmenu.h + ** Purpose: Definition of signal handler routines + ** Author: Dan Johnson + ** History: 10/2/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef SIGMENU_H +#define SIGMENU_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "cutil.h" +#include + +/* functions to be placed in the signal menu look like: */ +//typedef int (*SIG_MENU_FUNC)(...); +/* the value returned from a SIG_MENU_FUNC must be one of the following */ +#define SIG_RESUME 1 +#define SIG_MENU 0 + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void AddSignalMenuItem (int Signal, +int ItemNum, +const char ItemLabel[], int_void ItemFunc); +#endif diff --git a/classify/speckle.cpp b/classify/speckle.cpp new file mode 100644 index 0000000000..69106df15b --- /dev/null +++ b/classify/speckle.cpp @@ -0,0 +1,126 @@ +/****************************************************************************** + ** Filename: speckle.c + ** Purpose: Routines used by classifier to filter out speckle. + ** Author: Dan Johnson + ** History: Mon Mar 11 10:06:14 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "speckle.h" +#include "debug.h" +#include "blobs.h" + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* define control knobs for adjusting definition of speckle*/ +make_float_var (MaxLargeSpeckleSize, 0.30, MakeMaxLargeSpeckleSize, +16, 2, SetMaxLargeSpeckleSize, "Max Large Speckle Size ..."); + +make_float_var (SmallSpecklePenalty, 10.0, MakeSmallSpecklePenalty, +16, 3, SetSmallSpecklePenalty, "Small Speckle Penalty ..."); + +make_float_var (LargeSpecklePenalty, 10.0, MakeLargeSpecklePenalty, +16, 4, SetLargeSpecklePenalty, "Large Speckle Penalty ..."); + +make_float_var (SmallSpeckleCertainty, -1.0, MakeSmallSpeckleCertainty, +16, 5, SetSmallSpeckleCertainty, +"Small Speckle Certainty ..."); + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +LIST AddLargeSpeckleTo(LIST Choices) { +/* + ** Parameters: + ** Choices choices to add a speckle choice to + ** Globals: + ** SmallSpecklePenalty rating for a small speckle + ** LargeSpecklePenalty rating penalty for a large speckle + ** SmallSpeckleCertainty certainty for a small speckle + ** Operation: This routine adds a null choice to Choices with a + ** rating equal to the worst rating in Choices plus a pad. + ** The certainty of the new choice is the same as the + ** certainty of the worst choice in Choices. The new choice + ** is added to the end of Choices. + ** Return: New Choices list with null choice added to end. + ** Exceptions: none + ** History: Mon Mar 11 11:08:11 1991, DSJ, Created. + */ + LIST WorstChoice; + + /* if there are no other choices, use the small speckle penalty plus + the large speckle penalty */ + if (Choices == NIL) + return (append_choice (NIL, "", SmallSpecklePenalty + LargeSpecklePenalty, + SmallSpeckleCertainty, -1)); + + /* if there are other choices, add a null choice that is slightly worse + than the worst choice so far */ + WorstChoice = last (Choices); + return (append_choice (Choices, "", + best_probability (WorstChoice) + LargeSpecklePenalty, + best_certainty (WorstChoice), -1)); + +} /* AddLargeSpeckleTo */ + + +/*---------------------------------------------------------------------------*/ +void InitSpeckleVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Install the control variables needed for the speckle + ** filters. + ** Return: none + ** Exceptions: none + ** History: Mon Mar 11 12:04:59 1991, DSJ, Created. + */ + MakeMaxLargeSpeckleSize(); + MakeSmallSpecklePenalty(); + MakeLargeSpecklePenalty(); + MakeSmallSpeckleCertainty(); +} /* InitSpeckleVars */ + + +/*---------------------------------------------------------------------------*/ +BOOL8 LargeSpeckle(TBLOB *Blob, TEXTROW *Row) { +/* + ** Parameters: + ** Blob blob to test against speckle criteria + ** Row text row that blob is in + ** Globals: + ** MaxLargeSpeckleSize largest allowed speckle + ** Operation: This routine returns TRUE if both the width of height + ** of Blob are less than the MaxLargeSpeckleSize. + ** Return: TRUE if Blob is speckle, FALSE otherwise. + ** Exceptions: none + ** History: Mon Mar 11 10:06:49 1991, DSJ, Created. + */ + FLOAT32 SpeckleSize; + TPOINT TopLeft; + TPOINT BottomRight; + + SpeckleSize = RowHeight (Row) * MaxLargeSpeckleSize; + blob_bounding_box(Blob, &TopLeft, &BottomRight); + + if (TopLeft.y - BottomRight.y < SpeckleSize && + BottomRight.x - TopLeft.x < SpeckleSize) + return (TRUE); + else + return (FALSE); + +} /* LargeSpeckle */ diff --git a/classify/speckle.h b/classify/speckle.h new file mode 100644 index 0000000000..1f6de31a99 --- /dev/null +++ b/classify/speckle.h @@ -0,0 +1,69 @@ +/****************************************************************************** + ** Filename: speckle.h + ** Purpose: Interface to classifier speckle filtering routines. + ** Author: Dan Johnson + ** History: Mon Mar 11 10:14:16 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef SPECKLE_H +#define SPECKLE_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "baseline.h" +#include "choices.h" + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* macro for getting the height of a row of text */ +#define RowHeight(R) ((is_baseline_normalized ())? \ + (BASELINE_SCALE): \ + ((R)->lineheight)) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +LIST AddLargeSpeckleTo(LIST Choices); + +void InitSpeckleVars(); + +BOOL8 LargeSpeckle(TBLOB *Blob, TEXTROW *Row); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* speckle.c +LIST AddLargeSpeckleTo + _ARGS((LIST Choices)); + +void InitSpeckleVars + _ARGS((void)); + +BOOL8 LargeSpeckle + _ARGS((BLOB *Blob, + TEXTROW *Row)); + +#undef _ARGS +*/ +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern float SmallSpecklePenalty; +extern float SmallSpeckleCertainty; +#endif diff --git a/classify/xform2d.cpp b/classify/xform2d.cpp new file mode 100644 index 0000000000..90a172824e --- /dev/null +++ b/classify/xform2d.cpp @@ -0,0 +1,63 @@ +/****************************************************************************** + ** Filename: xform2d.c + ** Purpose: Library routines for performing 2D point transformations + ** Author: Dan Johnson + ** History: Fri Sep 22 09:54:17 1989, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "xform2d.h" +#include + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void RotateMatrix(MATRIX_2D_PTR Matrix, FLOAT32 Angle) { +/* + ** Parameters: + ** Matrix transformation matrix to rotate + ** Angle angle to rotate matrix + ** Globals: none + ** Operation: + ** Rotate the coordinate system (as specified by Matrix) about + ** its origin by Angle radians. In matrix notation the + ** effect is as follows: + ** + ** Matrix = R X Matrix + ** + ** where R is the following matrix + ** + ** cos Angle sin Angle 0 + ** -sin Angle cos Angle 0 + ** 0 0 1 + ** Return: none + ** Exceptions: none + ** History: 7/27/89, DSJ, Create. + */ + FLOAT32 Cos, Sin; + FLOAT32 NewA, NewB; + + Cos = cos ((double) Angle); + Sin = sin ((double) Angle); + + NewA = Matrix->a * Cos + Matrix->c * Sin; + NewB = Matrix->b * Cos + Matrix->d * Sin; + Matrix->c = Matrix->a * -Sin + Matrix->c * Cos; + Matrix->d = Matrix->b * -Sin + Matrix->d * Cos; + Matrix->a = NewA; + Matrix->b = NewB; + +} /* RotateMatrix */ diff --git a/classify/xform2d.h b/classify/xform2d.h new file mode 100644 index 0000000000..088a68158f --- /dev/null +++ b/classify/xform2d.h @@ -0,0 +1,69 @@ +/****************************************************************************** + ** Filename: xform2d.h + ** Purpose: Definitions for using 2D point transformation library + ** Author: Dan Johnson + ** History: Fri Sep 22 09:57:08 1989, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef XFORM2D_H +#define XFORM2D_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "fpoint.h" + +typedef struct +{ + FLOAT32 a, b, c, d, tx, ty; +} + + +MATRIX_2D, *MATRIX_2D_PTR; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* macros for initializing transform matrices */ +#define InitMatrix(M) ((M)->a = 1, (M)->b = 0, \ + (M)->c = 0, (M)->d = 1, \ + (M)->tx = 0, (M)->ty = 0 ) + +#define CopyMatrix(A,B) ((B)->a = (A)->a, (B)->b = (A)->b, \ + (B)->c = (A)->c, (B)->d = (A)->d, \ + (B)->tx = (A)->tx, (B)->ty = (A)->ty) + +/* matrix scaling, translation, rotation, mirroring, etc.*/ +#define TranslateMatrix(M,X,Y) ((M)->tx += (M)->a * (X) + (M)->c * (Y), \ + (M)->ty += (M)->b * (X) + (M)->d * (Y) ) + +#define ScaleMatrix(M,X,Y) ((M)->a *= (X), (M)->b *= (X), \ + (M)->c *= (Y), (M)->d *= (Y)) + +#define MirrorMatrixInX(M) (ScaleMatrix((M),-1,1)) +#define MirrorMatrixInY(M) (ScaleMatrix((M),1,-1)) +#define MirrorMatrixInXY(M) (ScaleMatrix((M),-1,-1)) + +/* using a matrix to map points*/ +#define MapX(M,X,Y) ((M)->a * (X) + (M)->c * (Y) + (M)->tx) +#define MapY(M,X,Y) ((M)->b * (X) + (M)->d * (Y) + (M)->ty) +#define MapPoint(M,A,B) (Xof(B) = MapX (M, Xof(A), Yof(A)), \ + Yof(B) = MapY (M, Xof(A), Yof(A))) +#define MapDx(M,DX,DY) ((M)->a * (DX) + (M)->c * (DY)) +#define MapDy(M,DX,DY) ((M)->b * (DX) + (M)->d * (DY)) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void RotateMatrix(MATRIX_2D_PTR Matrix, FLOAT32 Angle); +#endif diff --git a/config/ac_compile_check_sizeof.m4 b/config/ac_compile_check_sizeof.m4 new file mode 100644 index 0000000000..e0b62a8e49 --- /dev/null +++ b/config/ac_compile_check_sizeof.m4 @@ -0,0 +1,28 @@ +dnl Available from the GNU Autoconf Macro Archive at: +dnl http://www.gnu.org/software/ac-archive/htmldoc/ac_compile_check_sizeof.html +dnl +AC_DEFUN([AC_COMPILE_CHECK_SIZEOF], +[changequote(<<, >>)dnl +dnl The name to #define. +define(<>, translit(sizeof_$1, [a-z *], [A-Z_P]))dnl +dnl The cache variable name. +define(<>, translit(ac_cv_sizeof_$1, [ *], [_p]))dnl +changequote([, ])dnl +AC_MSG_CHECKING(size of $1) +AC_CACHE_VAL(AC_CV_NAME, +[for ac_size in 4 8 1 2 16 $2 ; do # List sizes in rough order of prevalence. + AC_TRY_COMPILE([#include "confdefs.h" +#include +$2 +], [switch (0) case 0: case (sizeof ($1) == $ac_size):;], AC_CV_NAME=$ac_size) + if test x$AC_CV_NAME != x ; then break; fi +done +]) +if test x$AC_CV_NAME = x ; then + AC_MSG_ERROR([cannot determine a size for $1]) +fi +AC_MSG_RESULT($AC_CV_NAME) +AC_DEFINE_UNQUOTED(AC_TYPE_NAME, $AC_CV_NAME, [The number of bytes in type $1]) +undefine([AC_TYPE_NAME])dnl +undefine([AC_CV_NAME])dnl +]) diff --git a/config/ac_create_stdint_h.m4 b/config/ac_create_stdint_h.m4 new file mode 100644 index 0000000000..468a3b8874 --- /dev/null +++ b/config/ac_create_stdint_h.m4 @@ -0,0 +1,552 @@ +##### http://autoconf-archive.cryp.to/ac_create_stdint_h.html +# +# OBSOLETE MACRO +# +# Replaced by AX_CREATE_STDINT_H. +# +# SYNOPSIS +# +# AC_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEDERS-TO-CHECK])] +# +# DESCRIPTION +# +# the "ISO C9X: 7.18 Integer types " section requires the +# existence of an include file that defines a set of +# typedefs, especially uint8_t,int32_t,uintptr_t. Many older +# installations will not provide this file, but some will have the +# very same definitions in . In other enviroments we can +# use the inet-types in which would define the typedefs +# int8_t and u_int8_t respectivly. +# +# This macros will create a local "_stdint.h" or the headerfile given +# as an argument. In many cases that file will just have a singular +# "#include " or "#include " statement, while +# in other environments it will provide the set of basic 'stdint's +# defined: +# int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t +# int_least32_t.. int_fast32_t.. intmax_t which may or may not rely +# on the definitions of other files, or using the +# AC_COMPILE_CHECK_SIZEOF macro to determine the actual sizeof each +# type. +# +# if your header files require the stdint-types you will want to +# create an installable file mylib-int.h that all your other +# installable header may include. So if you have a library package +# named "mylib", just use +# +# AC_CREATE_STDINT_H(mylib-int.h) +# +# in configure.in and go to install that very header file in +# Makefile.am along with the other headers (mylib.h) - and the +# mylib-specific headers can simply use "#include " to +# obtain the stdint-types. +# +# Remember, if the system already had a valid , the +# generated file will include it directly. No need for fuzzy +# HAVE_STDINT_H things... +# +# (note also the newer variant AX_CREATE_STDINT_H of this macro) +# +# LAST MODIFICATION +# +# 2006-10-13 +# +# COPYLEFT +# +# Copyright (c) 2006 Guido U. Draheim +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# As a special exception, the respective Autoconf Macro's copyright +# owner gives unlimited permission to copy, distribute and modify the +# configure scripts that are the output of Autoconf when processing +# the Macro. You need not follow the terms of the GNU General Public +# License when using or distributing such scripts, even though +# portions of the text of the Macro appear in them. The GNU General +# Public License (GPL) does govern all other use of the material that +# constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the +# Autoconf Macro released by the Autoconf Macro Archive. When you +# make and distribute a modified version of the Autoconf Macro, you +# may extend this special exception to the GPL to apply to your +# modified version as well. + +AC_DEFUN([AC_CREATE_STDINT_H], +[# ------ AC CREATE STDINT H ------------------------------------- +AC_MSG_CHECKING([for stdint-types....]) +ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)` +if test "$ac_stdint_h" = "stdint.h" ; then + AC_MSG_RESULT("(are you sure you want them in ./stdint.h?)") +elif test "$ac_stdint_h" = "inttypes.h" ; then + AC_MSG_RESULT("(are you sure you want them in ./inttypes.h?)") +else + AC_MSG_RESULT("(putting them into $ac_stdint_h)") +fi + +inttype_headers=`echo inttypes.h sys/inttypes.h sys/inttypes.h $2 \ +| sed -e 's/,/ /g'` + + ac_cv_header_stdint_x="no-file" + ac_cv_header_stdint_o="no-file" + ac_cv_header_stdint_u="no-file" + for i in stdint.h $inttype_headers ; do + unset ac_cv_type_uintptr_t + unset ac_cv_type_uint64_t + _AC_CHECK_TYPE_NEW(uintptr_t,[ac_cv_header_stdint_x=$i],dnl + continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="(uint64_t too)"],[and64=""],[#include<$i>]) + AC_MSG_RESULT(... seen our uintptr_t in $i $and64) + break; + done + if test "$ac_cv_header_stdint_x" = "no-file" ; then + for i in stdint.h $inttype_headers ; do + unset ac_cv_type_uint32_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],dnl + continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="(uint64_t too)"],[and64=""],[#include<$i>]) + AC_MSG_RESULT(... seen our uint32_t in $i $and64) + break; + done + if test "$ac_cv_header_stdint_o" = "no-file" ; then + for i in sys/types.h $inttype_headers ; do + unset ac_cv_type_u_int32_t + unset ac_cv_type_u_int64_t + AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],dnl + continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="(u_int64_t too)"],[and64=""],[#include<$i>]) + AC_MSG_RESULT(... seen our u_int32_t in $i $and64) + break; + done + fi + fi + +# ----------------- DONE inttypes.h checks MAYBE C basic types -------- + +if test "$ac_cv_header_stdint_x" = "no-file" ; then + AC_COMPILE_CHECK_SIZEOF(char) + AC_COMPILE_CHECK_SIZEOF(short) + AC_COMPILE_CHECK_SIZEOF(int) + AC_COMPILE_CHECK_SIZEOF(long) + AC_COMPILE_CHECK_SIZEOF(void*) + ac_cv_header_stdint_test="yes" +else + ac_cv_header_stdint_test="no" +fi + +# ----------------- DONE inttypes.h checks START header ------------- +_ac_stdint_h=AS_TR_CPP(_$ac_stdint_h) +AC_MSG_RESULT(creating $ac_stdint_h : $_ac_stdint_h) +echo "#ifndef" $_ac_stdint_h >$ac_stdint_h +echo "#define" $_ac_stdint_h "1" >>$ac_stdint_h +echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint_h +echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint_h +if test "$GCC" = "yes" ; then + echo "/* generated using a gnu compiler version" `$CC --version` "*/" \ + >>$ac_stdint_h +else + echo "/* generated using $CC */" >>$ac_stdint_h +fi +echo "" >>$ac_stdint_h + +if test "$ac_cv_header_stdint_x" != "no-file" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_x" +elif test "$ac_cv_header_stdint_o" != "no-file" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_o" +elif test "$ac_cv_header_stdint_u" != "no-file" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_u" +else + ac_cv_header_stdint="stddef.h" +fi + +# ----------------- See if int_least and int_fast types are present +unset ac_cv_type_int_least32_t +unset ac_cv_type_int_fast32_t +AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>]) +AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>]) + +if test "$ac_cv_header_stdint" != "stddef.h" ; then +if test "$ac_cv_header_stdint" != "stdint.h" ; then +AC_MSG_RESULT(..adding include stddef.h) + echo "#include " >>$ac_stdint_h +fi ; fi +AC_MSG_RESULT(..adding include $ac_cv_header_stdint) + echo "#include <$ac_cv_header_stdint>" >>$ac_stdint_h +echo "" >>$ac_stdint_h + +# ----------------- DONE header START basic int types ------------- +if test "$ac_cv_header_stdint_x" = "no-file" ; then + AC_MSG_RESULT(... need to look at C basic types) +dnl ac_cv_header_stdint_test="yes" # moved up before creating the file +else + AC_MSG_RESULT(... seen good stdint.h inttypes) +dnl ac_cv_header_stdint_test="no" # moved up before creating the file +fi + +if test "$ac_cv_header_stdint_u" != "no-file" ; then + AC_MSG_RESULT(... seen bsd/sysv typedefs) + cat >>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h < 199901L + +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +#elif !defined __STRICT_ANSI__ +#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ + +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif + +#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ +dnl /* note: all ELF-systems seem to have loff-support which needs 64-bit */ + +#if !defined _NO_LONGLONG +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif +#endif + +#elif defined __alpha || (defined __mips && defined _ABIN32) + +#if !defined _NO_LONGLONG +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef long int64_t; +typedef unsigned long uint64_t; +#endif +#endif + /* compiler/cpu type ... or just ISO C99 */ +#endif +#endif +EOF + +# plus a default 64-bit for systems that are likely to be 64-bit ready + case "$ac_cv_sizeof_x:$ac_cv_sizeof_voidp:$ac_cv_sizeof_long" in + 1:2:8:8) AC_MSG_RESULT(..adding uint64_t default, normal 64-bit system) +cat >>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h < conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + AC_MSG_RESULT(yes) + rm conftest.* + $2 + else + AC_MSG_RESULT(no) + rm conftest.* + $3 + fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CC_OPTIMIZE +dnl Setup option --enable-debug +dnl Collects optimization/debug option in variable CFLAGS, +dnl filtering options already in CFLAGS. Also define +dnl DEBUG_MODE if appropriate. +dnl Adapted from AC_CXX_OPTIMIZE in djvulibre +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_OPTIMIZE],[ + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_ARG_ENABLE(debug, + AC_HELP_STRING([--enable-debug], + [Compile with debugging options (default: no)]), + [ac_debug=$enableval],[ac_debug=no]) + OPTS= + AC_SUBST(OPTS) + dnl VCOPTS_COMMON="/nologo /G5 /Zp1 /W3 /Za /Op- /GX" + # Note: the /Za option might be nice to have, but it is + # incompatible with , which some packages + # (like the tiff library) require + VCOPTS_COMMON="/nologo /G5 /Zp1 /W3" + dnl AC_SUBST(OPTS) + saved_CXXFLAGS="$CXXFLAGS" + saved_CFLAGS="$CFLAGS" + CXXFLAGS= + CFLAGS= + for opt in $saved_CXXFLAGS ; do + case $opt in + -g*) test $ac_debug != no && OPTS="$OPTS $opt" ;; + -O*) ;; + *) CXXFLAGS="$CXXFLAGS $opt" ;; + esac + done + for opt in $saved_CFLAGS ; do + case $opt in + -O*|-g*) ;; + *) CFLAGS="$CFLAGS $opt" ;; + esac + done + if test x$ac_debug = xno ; then + OPTS=-DNDEBUG + if test x$CXX = xcl.exe ; then + OPTS="$OPTS $VCOPTS_COMMON /Ow /O2" + else + AC_CHECK_CXX_OPT([-O3],[OPTS="$OPTS -O3"], + [ AC_CHECK_CXX_OPT([-O2], [OPTS="$OPTS -O2"] ) ] ) + dnl This triggers compiler bugs with gcc-3.2.2 - comment out for now + dnl AC_CHECK_CXX_OPT([-funroll-loops], [OPTS="$OPTS -funroll-loops"]) + cpu=`uname -m 2>/dev/null` + test -z "$cpu" && cpu=${host_cpu} + case "${host_cpu}" in + i?86) + opt="-mcpu=${host_cpu}" + AC_CHECK_CXX_OPT([$opt], [OPTS="$OPTS $opt"]) + ;; + esac + fi + else + if test x$CXX = xcl.exe ; then + OPTS="$OPTS $VCOPTS_COMMON /Od /Z7" + fi + AC_DEFINE(DEBUG_MODE,1,[Define when compiling in debug mode]) + fi + if test x$CXX != xcl.exe ; then + AC_CHECK_CXX_OPT([-Wall],[OPTS="$OPTS -Wall"]) + fi + case x"$ac_debug" in +changequote(<<, >>)dnl + x[0-9]) OPTS="$OPTS -DDEBUGLVL=$ac_debug" ;; + xr*) OPTS="$OPTS -DRUNTIME_DEBUG_ONLY" ;; +changequote([, ])dnl + esac + CXXFLAGS="$CXXFLAGS $OPTS" + CFLAGS="$CFLAGS $OPTS" +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_MEMBER_TEMPLATES +dnl If the compiler supports member templates, +dnl define HAVE_MEMBER_TEMPLATES. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_MEMBER_TEMPLATES], +[AC_CACHE_CHECK(whether the compiler supports member templates, +ac_cv_cxx_member_templates, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([ +template class A +{ public: + template A operator=(const A& z) { return A(); } +};],[A x; A y; x = y; return 0;], + ac_cv_cxx_member_templates=yes, ac_cv_cxx_member_templates=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_member_templates" = yes; then + AC_DEFINE(HAVE_MEMBER_TEMPLATES,1, + [define if the compiler supports member templates]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_NAMESPACES +dnl Define HAVE_NAMESPACES if the compiler supports +dnl namespaces. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_NAMESPACES], +[AC_CACHE_CHECK(whether the compiler implements namespaces, +ac_cv_cxx_namespaces, +[ AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([namespace Outer { namespace Inner { int i = 0; }}], + [using namespace Outer::Inner; return i;], + ac_cv_cxx_namespaces=yes, ac_cv_cxx_namespaces=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_namespaces" = yes && test "$ac_debug" = no; then + AC_DEFINE(HAVE_NAMESPACES,1, + [define if the compiler implements namespaces]) +fi +]) + + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_TYPENAME +dnl Define HAVE_TYPENAME if the compiler recognizes +dnl keyword typename. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_TYPENAME], +[AC_CACHE_CHECK(whether the compiler recognizes typename, +ac_cv_cxx_typename, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([templateclass X {public:X(){}};], +[X z; return 0;], + ac_cv_cxx_typename=yes, ac_cv_cxx_typename=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_typename" = yes; then + AC_DEFINE(HAVE_TYPENAME,1,[define if the compiler recognizes typename]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_STDINCLUDES +dnl Define HAVE_STDINCLUDES if the compiler has the +dnl new style include files (without the .h) +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_STDINCLUDES], +[AC_CACHE_CHECK(whether the compiler comes with standard includes, +ac_cv_cxx_stdincludes, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([#include +struct X { int a; X(int a):a(a){}; }; +X* foo(void *x) { return new(x) X(2); } ],[], + ac_cv_cxx_stdincludes=yes, ac_cv_cxx_stdincludes=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_stdincludes" = yes; then + AC_DEFINE(HAVE_STDINCLUDES,1, + [define if the compiler comes with standard includes]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_BOOL +dnl If the compiler recognizes bool as a separate built-in type, +dnl define HAVE_BOOL. Note that a typedef is not a separate +dnl type since you cannot overload a function such that it +dnl accepts either the basic type or the typedef. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_BOOL], +[AC_CACHE_CHECK(whether the compiler recognizes bool as a built-in type, +ac_cv_cxx_bool, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([ +int f(int x){return 1;} +int f(char x){return 1;} +int f(bool x){return 1;} +],[bool b = true; return f(b);], + ac_cv_cxx_bool=yes, ac_cv_cxx_bool=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_bool" = yes; then + AC_DEFINE(HAVE_BOOL,1,[define if bool is a built-in type]) +fi +]) + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_EXCEPTIONS +dnl If the C++ compiler supports exceptions handling (try, +dnl throw and catch), define HAVE_EXCEPTIONS. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_EXCEPTIONS], +[AC_CACHE_CHECK(whether the compiler supports exceptions, +ac_cv_cxx_exceptions, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE(,[try { throw 1; } catch (int i) { return i; }], + ac_cv_cxx_exceptions=yes, ac_cv_cxx_exceptions=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_exceptions" = yes; then + AC_DEFINE(HAVE_EXCEPTIONS,1,[define if the compiler supports exceptions]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_RPO +dnl Defines option --enable-rpo and searches program RPO. +dnl Set output variables CXXRPOFLAGS and RPO. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_RPO], +[ CXXRPOFLAGS= + RPO_YES='#' + RPO_NO='' + if test x$GXX = xyes ; then + AC_ARG_ENABLE([rpo], + AC_HELP_STRING([--enable-rpo], + [Enable compilation with option -frepo]), + [ac_rpo=$enableval], [ac_rpo=no] ) + if test x$ac_rpo != xno ; then + CXXRPOFLAGS='-frepo -fno-rtti' + RPO_YES='' + RPO_NO='#' + fi + fi + AC_SUBST(CXXRPOFLAGS) + AC_SUBST(RPO_YES) + AC_SUBST(RPO_NO) +]) + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_GNUWIN32([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Process option --with-gnuwin32. +dnl Try to determine where GNUWIN32 is located +dnl Define GNUWIN32 accordingly (either blank or PATH) +dnl Also update the LDFLAGS and CFLAGS accordingly +dnl ------------------------------------------------------------------ +AC_DEFUN([AC_PATH_GNUWIN32], +[ + # Test whether we are running on Windows. This test is becoming + # useless because other environments exist under Windows, such + # as mingw, whihc gives host i686-pc-mingw32. Just remove this test + # for now by setting variable to yes all the time. +# AC_REQUIRE([AC_CANONICAL_HOST])[]dnl +# case $host_os in +# *cygwin* ) USING_CYGWIN_OR_MINGW=yes;; +# *mingw* ) USING_CYGWIN_OR_MINGW=yes;; +# * ) USING_CYGWIN_OR_MINGW=no;; +# esac + USING_CYGWIN_OR_MINGW=yes + # If running on Windows, do some tests + if test x$USING_CYGWIN_OR_MINGW = xyes ; then + AC_ARG_VAR(GNUWIN32_DIR,[Base directory of GnuWin32 packages]) + ac_gnuwin32=no + AC_ARG_WITH(gnuwin32, + AC_HELP_STRING([--with-gnuwin32=DIR], + [where the GnuWin32 packages are installed]), + [ac_gnuwin32=$withval], [ac_gnuwin32=yes] ) + # Process specification + AC_MSG_CHECKING([for GnuWin32 directory]) + if test x$ac_gnuwin32 = xyes ; then + # GNUWIN32_BASE could have been set on the command line + if test x$GNUWIN32_BASE != x ; then + if test -d "$GNUWIN32_BASE" ; then + AC_MSG_RESULT([verified at $GNUWIN32_BASE]) + else + # AC_MSG_RESULT([no GnuWin32 at $GNUWIN32_BASE. Looking elsewhere]) + GNUWIN32_BASE= + fi + # Otherwise, GNUWIN32 is an environment variable that the user can set + elif test x$GNUWIN32 != x ; then + if test -d "$GNUWIN32" ; then + GNUWIN32_BASE=$GNUWIN32 + AC_MSG_RESULT([verified at $GNUWIN32_BASE]) + else + # AC_MSG_RESULT([no GnuWin32 at $GNUWIN32. Looking elsewhere]) + GNUWIN32_BASE= + fi + fi + # Look in default locations if needed: + if test x$GNUWIN32_BASE = x ; then + if test -d "C:/Program Files/GnuWin32" ; then + GNUWIN32_BASE="C:/Program Files/GnuWin32" + AC_MSG_RESULT([$GNUWIN32_BASE]) + elif test -d "C:/pckg/GnuWin32" ; then + GNUWIN32_BASE="C:/pckg/GnuWin32" + AC_MSG_RESULT([$GNUWIN32_BASE]) + else + AC_MSG_RESULT([not found]) + fi + fi + # If directory location specified on command line + elif test x$ac_gnuwin32 != xno ; then + if test -d "$ac_gnuwin32" ; then + GNUWIN32_BASE="$ac_gnuwin32" + AC_MSG_RESULT([verified at $GNUWIN32_BASE]) + else + GNUWIN32_BASE= + AC_MSG_RESULT([not found]) + fi + fi + # Now we can update LDFLAGS, CFLAGS and CXXFLAGS + # Do it in such a way that GnuWin32 has precedence + # over system includes and libraries + if test x$GNUWIN32_BASE != x ; then + CFLAGS="-I$GNUWIN32_BASE/include $CFLAGS" + CXXFLAGS="-I$GNUWIN32_BASE/include $CXXFLAGS" + if test x$CXX != xcl.exe ; then + LDFLAGS="-L$GNUWIN32_BASE/lib $LDFLAGS" + fi + fi + # If not running on cygwin, GNUWIN32 is useless + else + GNUWIN32_BASE= + fi + # Finally, sets automake conditional + AM_CONDITIONAL(HAVE_GNUWIN32, test x$GNUWIN32_BASE != x) +]) + + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_JPEG([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Process option --with-jpeg. +dnl Search JPEG along the extra +dnl Define HAVE_JPEG. +dnl Set output variable JPEG_CFLAGS and JPEG_LIBS +dnl Designed by Leon. Unused at the moment. +dnl ------------------------------------------------------------------ + +AC_DEFUN([AC_PATH_JPEG], +[ + AC_ARG_VAR(JPEG_LIBS) + AC_ARG_VAR(JPEG_CFLAGS) + ac_jpeg=no + AC_ARG_WITH(jpeg, + AC_HELP_STRING([--with-jpeg=DIR], + [where the IJG jpeg library is located]), + [ac_jpeg=$withval], [ac_jpeg=yes] ) + # Process specification + if test x$ac_jpeg = xyes ; then + test x${JPEG_LIBS+set} != xset && JPEG_LIBS="-ljpeg" + elif test x$ac_jpeg != xno ; then + test x${JPEG_LIBS+set} != xset && JPEG_LIBS="-L$ac_jpeg -ljpeg" + test x${JPEG_CFLAGS+set} != xset && JPEG_CFLAGS="-I$ac_jpeg" + fi + # Try linking + if test x$ac_jpeg != xno ; then + AC_MSG_CHECKING([for jpeg library]) + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LIBS="$LIBS" + CFLAGS="$CFLAGS $JPEG_CFLAGS" + CXXFLAGS="$CXXFLAGS $JPEG_CFLAGS" + LIBS="$LIBS $JPEG_LIBS" + AC_TRY_LINK([ +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif ],[ +jpeg_CreateDecompress(0,0,0);], + [ac_jpeg=yes], [ac_jpeg=no] ) + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + LIBS="$save_LIBS" + AC_MSG_RESULT($ac_jpeg) + fi + # Finish + if test x$ac_jpeg = xno; then + JPEG_CFLAGS= ; JPEG_LIBS= + ifelse([$2],,:,[$2]) + else + AC_DEFINE(HAVE_JPEG,1,[Define if you have the IJG JPEG library.]) + AC_MSG_RESULT([setting JPEG_CFLAGS=$JPEG_CFLAGS]) + AC_MSG_RESULT([setting JPEG_LIBS=$JPEG_LIBS]) + ifelse([$1],,:,[$1]) + fi +]) + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_LIBTIFF([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Process option --with-libtiff. +dnl Search LIBTIFF along the extra +dnl Define HAVE_LIBTIFF. +dnl Set output variable LIBTIFF_CFLAGS and LIBTIFF_LIBS +dnl -- inspired from previous AC_PATH_JPEG +dnl ------------------------------------------------------------------ +AC_DEFUN([AC_PATH_LIBTIFF], +[ + AC_REQUIRE([AC_CANONICAL_HOST]) + case $host in + *msdos* | *go32* | *mingw32* | *cygwin* | *windows*) + USING_WIN=yes + ;; + *) + USING_WIN=no + esac + AC_REQUIRE([AC_PATH_GNUWIN32]) + AC_ARG_VAR(LIBTIFF_LIBS,[Tiff library to link against]) + AC_ARG_VAR(LIBTIFF_CFLAGS,[Compile flags needed for TIFF support]) + ac_libtiff=no + AC_ARG_WITH(libtiff, + AC_HELP_STRING([--with-libtiff=DIR], + [where the www.libtiff.org libtiff library is located]), + [ac_libtiff=$withval], [ac_libtiff=yes] ) + AC_MSG_CHECKING([for Leffler libtiff library]) + # Need to define TOP_SRCDIR for the cases where the LIBTIFF library + # is checked in with the code, at the top-level + TOP_SRCDIR=`cd $srcdir; pwd` + LOCAL_LIBTIFFDIR=$TOP_SRCDIR/libtiff + # First of all, deal with the case when 'cl.exe' is used as compiler + # indeed, when this is the case, we can only rely on finding + # the right library and includes, but we can't try compiling + # and linking. In addition, library needs to be specifically + # supplied on the link line and special flags are needed... + if test "x$CXX" = "xcl.exe" ; then + if test "x$ac_libtiff" = "xyes" ; then + # If LIBTIFF_LIBS has been set on configure command line + # or as environment variable, just use it if it exists + if test "x$LIBTIFF_LIBS" != "x" ; then + AC_MSG_RESULT(user specified as $LIBTIFF_LIBS) + if test "x$LIBTIFF_CFLAGS" = "x" ; then + AC_MSG_WARN(LIBTIFF_CFLAGS is empty) + fi + fi + # Otherwise, test if libtiff is at top level directory, under libtiff + if test "x$LIBTIFF_LIBS" = "x" && test -r "$LOCAL_LIBTIFFDIR/lib/libtiff.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libjpeg.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libz.a" ; then + AC_MSG_RESULT($LOCAL_LIBTIFFDIR/lib/libtiff.a) + LIBTIFF_LIBS="$LOCAL_LIBTIFFDIR/lib/libtiff.a $LOCAL_LIBTIFFDIR/lib/libjpeg.a $LOCAL_LIBTIFFDIR/lib/libz.a user32.lib" + LIBTIFF_CFLAGS="-I$LOCAL_LIBTIFFDIR/include" + fi + # Otherwise, if GnuWin32 was previously located + if test "x$LIBTIFF_LIBS" = "x" && test -r "$GNUWIN32_BASE/lib/libtiff.a" && test -r "$GNUWIN32_BASE/lib/libjpeg.a" && test -r "$GNUWIN32_BASE/lib/libz.a" ; then + AC_MSG_RESULT($GNUWIN32_BASE/lib/libtiff.a) + AC_MSG_WARN($GNUWIN32_BASE/lib/libtiff.a version 3.5.7 works. Some versions like 3.6.1 do not!) + LIBTIFF_LIBS="$GNUWIN32_BASE/lib/libtiff.a $GNUWIN32_BASE/lib/libjpeg.a $GNUWIN32_BASE/lib/libz.a user32.lib" + fi + if test "x$LIBTIFF_LIBS" = "x" ; then + AC_MSG_RESULT(not found or incomplete) + ac_libtiff=no + fi + elif test "x$ac_libtiff" != "xno" ; then + test x${LIBTIFF_LIBS+set} != xset && LIBTIFF_LIBS="$ac_libtiff/libtiff" + test x${LIBTIFF_CFLAGS+set} != xset && LIBTIFF_CFLAGS="-I$ac_libtiff" + fi + # If we are not using CL, that is we are either using gcc/cygwin + # or we are not running under Windows: + else + # Process specification + if test "x$ac_libtiff" = "xyes" ; then + if test "x$LIBTIFF_LIBS" = "x" ; then + # If local libtiff exists at top level, and we are running windows, use it first: + if test "x$USING_WIN" = "xyes" && test -r "$LOCAL_LIBTIFFDIR/lib/libtiff.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libjpeg.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libz.a" ; then + AC_MSG_RESULT($LOCAL_LIBTIFFDIR/lib/libtiff.a) + LIBTIFF_LIBS="$LOCAL_LIBTIFFDIR/lib/libtiff.a $LOCAL_LIBTIFFDIR/lib/libjpeg.a $LOCAL_LIBTIFFDIR/lib/libz.a -lole32 -luuid -lwsock32" + LIBTIFF_CFLAGS="-I$LOCAL_LIBTIFFDIR/include" + fi + # If GNUWIN32_BASE is defined, it means we are running under + # Windows and we should use these builds if available because + # they are the best bet + if test "x$LIBTIFF_LIBS" = "x" && test "x$GNUWIN32_BASE" != "x" ; then + # With version 3.5.7 of gnuwin32 libtiff, we do not need to link + # against "$GNUWIN32_BASE/lib/libgw32c.a", so no need to test + if test -r "$GNUWIN32_BASE/lib/libtiff.a" && test -r "$GNUWIN32_BASE/lib/libjpeg.a" && test -r "$GNUWIN32_BASE/lib/libz.a" ; then + AC_MSG_RESULT($GNUWIN32_BASE/lib/libtiff.a) + # With 3.5.7 version of libtiff, no need to add + # $GNUWIN32_BASE/lib/libgw32c.a after $GNUWIN32_BASE/lib/libz.a + LIBTIFF_LIBS="$GNUWIN32_BASE/lib/libtiff.a $GNUWIN32_BASE/lib/libjpeg.a $GNUWIN32_BASE/lib/libz.a -lole32 -luuid -lwsock32" + else + AC_MSG_RESULT([GNUWIN32 not found or incomplete. Trying something else]) + fi + fi + # If LIBTIFF_LIBS is still not defined after potentially going + # the GNUWIN32 route, then try using home built versions + if test "x$LIBTIFF_LIBS" = "x" ; then + TIFF_PACKAGE="$HOME_UNIX/packages/libtiff/libtiff" + if test -d "$TIFF_PACKAGE" ; then + LIBTIFF_LIBS="$TIFF_PACKAGE/libtiff.a" + LIBTIFF_CFLAGS="-I$TIFF_PACKAGE" + else + LIBTIFF_LIBS="-ltiff" + fi + fi + fi + elif test "x$ac_libtiff" != "xno" ; then + test "x${LIBTIFF_LIBS+set}" != "xset" && LIBTIFF_LIBS="$ac_libtiff/libtiff.a" + test "x${LIBTIFF_CFLAGS+set}" != "xset" && LIBTIFF_CFLAGS="-I$ac_libtiff" + fi + # Try linking - recall that -Wall is typically on, and any warning + # will cause this test to fail... This, by the way, is one of the + # reasons we cannot run this test when using the Microsoft compiler (cl), + # which outputs tons of "garbage" on stdout: + if test "x$ac_libtiff" != "xno" ; then + AC_MSG_CHECKING([linking with $LIBTIFF_LIBS]) + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LIBS="$LIBS" + CFLAGS="$CFLAGS $LIBTIFF_CFLAGS" + CXXFLAGS="$CXXFLAGS $LIBTIFF_CFLAGS" + LIBS="$LIBS $LIBTIFF_LIBS -lm" + AC_TRY_LINK([ +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif ],[ +TIFFClose((TIFF *) 0);], + [ac_libtiff=ok],[ac_libtiff=no]) + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + LIBS="$save_LIBS" + AC_MSG_RESULT($ac_libtiff) + fi + fi + # Finish + if test "x$ac_libtiff" = "xno"; then + LIBTIFF_CFLAGS= ; LIBTIFF_LIBS= + ifelse([$2],,:,[$2]) + else + AC_DEFINE(HAVE_LIBTIFF,1,[Define if you have the www.libtiff.org LIBTIFF library.]) + AC_MSG_RESULT([setting LIBTIFF_CFLAGS=$LIBTIFF_CFLAGS]) + AC_MSG_RESULT([setting LIBTIFF_LIBS=$LIBTIFF_LIBS]) + CFLAGS="$LIBTIFF_CFLAGS $CFLAGS" + CXXFLAGS="$LIBTIFF_CFLAGS $CXXFLAGS" + if test "x$CXX" = "xcl.exe" ; then + LIBS="$LIBTIFF_LIBS $LIBS" + else + LIBS="$LIBTIFF_LIBS $LIBS -lm" + fi + ifelse([$1],,:,[$1]) + fi + AM_CONDITIONAL(HAVE_LIBTIFF, test x$ac_libtiff != xno) +]) diff --git a/config/ax_create_stdint_h.m4 b/config/ax_create_stdint_h.m4 new file mode 100644 index 0000000000..97df2bb61b --- /dev/null +++ b/config/ax_create_stdint_h.m4 @@ -0,0 +1,734 @@ +##### http://autoconf-archive.cryp.to/ax_create_stdint_h.html +# +# SYNOPSIS +# +# AX_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEDERS-TO-CHECK])] +# +# DESCRIPTION +# +# the "ISO C9X: 7.18 Integer types " section requires the +# existence of an include file that defines a set of +# typedefs, especially uint8_t,int32_t,uintptr_t. Many older +# installations will not provide this file, but some will have the +# very same definitions in . In other enviroments we can +# use the inet-types in which would define the typedefs +# int8_t and u_int8_t respectivly. +# +# This macros will create a local "_stdint.h" or the headerfile given +# as an argument. In many cases that file will just "#include +# " or "#include ", while in other environments +# it will provide the set of basic 'stdint's definitions/typedefs: +# +# int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t +# int_least32_t.. int_fast32_t.. intmax_t +# +# which may or may not rely on the definitions of other files, or +# using the AC_CHECK_SIZEOF macro to determine the actual sizeof each +# type. +# +# if your header files require the stdint-types you will want to +# create an installable file mylib-int.h that all your other +# installable header may include. So if you have a library package +# named "mylib", just use +# +# AX_CREATE_STDINT_H(mylib-int.h) +# +# in configure.ac and go to install that very header file in +# Makefile.am along with the other headers (mylib.h) - and the +# mylib-specific headers can simply use "#include " to +# obtain the stdint-types. +# +# Remember, if the system already had a valid , the +# generated file will include it directly. No need for fuzzy +# HAVE_STDINT_H things... (oops, GCC 4.2.x has deliberatly disabled +# its stdint.h for non-c99 compilation and the c99-mode is not the +# default. Therefore this macro will not use the compiler's stdint.h +# - please complain to the GCC developers). +# +# LAST MODIFICATION +# +# 2006-10-13 +# +# COPYLEFT +# +# Copyright (c) 2006 Guido U. Draheim +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# As a special exception, the respective Autoconf Macro's copyright +# owner gives unlimited permission to copy, distribute and modify the +# configure scripts that are the output of Autoconf when processing +# the Macro. You need not follow the terms of the GNU General Public +# License when using or distributing such scripts, even though +# portions of the text of the Macro appear in them. The GNU General +# Public License (GPL) does govern all other use of the material that +# constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the +# Autoconf Macro released by the Autoconf Macro Archive. When you +# make and distribute a modified version of the Autoconf Macro, you +# may extend this special exception to the GPL to apply to your +# modified version as well. + +AC_DEFUN([AX_CHECK_DATA_MODEL],[ + AC_CHECK_SIZEOF(char) + AC_CHECK_SIZEOF(short) + AC_CHECK_SIZEOF(int) + AC_CHECK_SIZEOF(long) + AC_CHECK_SIZEOF(void*) + ac_cv_char_data_model="" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_char" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_short" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_long" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_voidp" + AC_MSG_CHECKING([data model]) + case "$ac_cv_char_data_model/$ac_cv_long_data_model" in + 122/242) ac_cv_data_model="IP16" ; n="standard 16bit machine" ;; + 122/244) ac_cv_data_model="LP32" ; n="standard 32bit machine" ;; + 122/*) ac_cv_data_model="i16" ; n="unusual int16 model" ;; + 124/444) ac_cv_data_model="ILP32" ; n="standard 32bit unixish" ;; + 124/488) ac_cv_data_model="LP64" ; n="standard 64bit unixish" ;; + 124/448) ac_cv_data_model="LLP64" ; n="unusual 64bit unixish" ;; + 124/*) ac_cv_data_model="i32" ; n="unusual int32 model" ;; + 128/888) ac_cv_data_model="ILP64" ; n="unusual 64bit numeric" ;; + 128/*) ac_cv_data_model="i64" ; n="unusual int64 model" ;; + 222/*2) ac_cv_data_model="DSP16" ; n="strict 16bit dsptype" ;; + 333/*3) ac_cv_data_model="DSP24" ; n="strict 24bit dsptype" ;; + 444/*4) ac_cv_data_model="DSP32" ; n="strict 32bit dsptype" ;; + 666/*6) ac_cv_data_model="DSP48" ; n="strict 48bit dsptype" ;; + 888/*8) ac_cv_data_model="DSP64" ; n="strict 64bit dsptype" ;; + 222/*|333/*|444/*|666/*|888/*) : + ac_cv_data_model="iDSP" ; n="unusual dsptype" ;; + *) ac_cv_data_model="none" ; n="very unusual model" ;; + esac + AC_MSG_RESULT([$ac_cv_data_model ($ac_cv_long_data_model, $n)]) +]) + +dnl AX_CHECK_HEADER_STDINT_X([HEADERLIST][,ACTION-IF]) +AC_DEFUN([AX_CHECK_HEADER_STDINT_X],[ +AC_CACHE_CHECK([for stdint uintptr_t], [ac_cv_header_stdint_x],[ + ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[stdint.h inttypes.h sys/inttypes.h sys/types.h]) + do + unset ac_cv_type_uintptr_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uintptr_t,[ac_cv_header_stdint_x=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$1],[$1]) break + done + AC_MSG_CHECKING([for stdint uintptr_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_O],[ +AC_CACHE_CHECK([for stdint uint32_t], [ac_cv_header_stdint_o],[ + ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[inttypes.h sys/inttypes.h sys/types.h stdint.h]) + do + unset ac_cv_type_uint32_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$1],[$1]) break + break; + done + AC_MSG_CHECKING([for stdint uint32_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_U],[ +AC_CACHE_CHECK([for stdint u_int32_t], [ac_cv_header_stdint_u],[ + ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[sys/types.h inttypes.h sys/inttypes.h]) ; do + unset ac_cv_type_u_int32_t + unset ac_cv_type_u_int64_t + AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(u_int64_t,[and64="/u_int64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$1],[$1]) break + break; + done + AC_MSG_CHECKING([for stdint u_int32_t]) + ]) +]) + +AC_DEFUN([AX_CREATE_STDINT_H], +[# ------ AX CREATE STDINT H ------------------------------------- +AC_MSG_CHECKING([for stdint types]) +ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)` +# try to shortcircuit - if the default include path of the compiler +# can find a "stdint.h" header then we assume that all compilers can. +AC_CACHE_VAL([ac_cv_header_stdint_t],[ +old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS="" +old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS="" +old_CFLAGS="$CFLAGS" ; CFLAGS="" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[ac_cv_stdint_result="(assuming C99 compatible system)" + ac_cv_header_stdint_t="stdint.h"; ], +[ac_cv_header_stdint_t=""]) +if test "$GCC" = "yes" && test ".$ac_cv_header_stdint_t" = "."; then +CFLAGS="-std=c99" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[AC_MSG_WARN(your GCC compiler has a defunct stdint.h for its default-mode)]) +fi +CXXFLAGS="$old_CXXFLAGS" +CPPFLAGS="$old_CPPFLAGS" +CFLAGS="$old_CFLAGS" ]) + +v="... $ac_cv_header_stdint_h" +if test "$ac_stdint_h" = "stdint.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./stdint.h?)]) +elif test "$ac_stdint_h" = "inttypes.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./inttypes.h?)]) +elif test "_$ac_cv_header_stdint_t" = "_" ; then + AC_MSG_RESULT([(putting them into $ac_stdint_h)$v]) +else + ac_cv_header_stdint="$ac_cv_header_stdint_t" + AC_MSG_RESULT([$ac_cv_header_stdint (shortcircuit)]) +fi + +if test "_$ac_cv_header_stdint_t" = "_" ; then # can not shortcircuit.. + +dnl .....intro message done, now do a few system checks..... +dnl btw, all old CHECK_TYPE macros do automatically "DEFINE" a type, +dnl therefore we use the autoconf implementation detail CHECK_TYPE_NEW +dnl instead that is triggered with 3 or more arguments (see types.m4) + +inttype_headers=`echo $2 | sed -e 's/,/ /g'` + +ac_cv_stdint_result="(no helpful system typedefs seen)" +AX_CHECK_HEADER_STDINT_X(dnl + stdint.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen uintptr_t$and64 in $i)") + +if test "_$ac_cv_header_stdint_x" = "_" ; then +AX_CHECK_HEADER_STDINT_O(dnl, + inttypes.h sys/inttypes.h stdint.h $inttype_headers, + ac_cv_stdint_result="(seen uint32_t$and64 in $i)") +fi + +if test "_$ac_cv_header_stdint_x" = "_" ; then +if test "_$ac_cv_header_stdint_o" = "_" ; then +AX_CHECK_HEADER_STDINT_U(dnl, + sys/types.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen u_int32_t$and64 in $i)") +fi fi + +dnl if there was no good C99 header file, do some typedef checks... +if test "_$ac_cv_header_stdint_x" = "_" ; then + AC_MSG_CHECKING([for stdint datatype model]) + AC_MSG_RESULT([(..)]) + AX_CHECK_DATA_MODEL +fi + +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_x" +elif test "_$ac_cv_header_stdint_o" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_o" +elif test "_$ac_cv_header_stdint_u" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_u" +else + ac_cv_header_stdint="stddef.h" +fi + +AC_MSG_CHECKING([for extra inttypes in chosen header]) +AC_MSG_RESULT([($ac_cv_header_stdint)]) +dnl see if int_least and int_fast types are present in _this_ header. +unset ac_cv_type_int_least32_t +unset ac_cv_type_int_fast32_t +AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>]) +AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>]) +AC_CHECK_TYPE(intmax_t,,,[#include <$ac_cv_header_stdint>]) + +fi # shortcircut to system "stdint.h" +# ------------------ PREPARE VARIABLES ------------------------------ +if test "$GCC" = "yes" ; then +ac_cv_stdint_message="using gnu compiler "`$CC --version | head -1` +else +ac_cv_stdint_message="using $CC" +fi + +AC_MSG_RESULT([make use of $ac_cv_header_stdint in $ac_stdint_h dnl +$ac_cv_stdint_result]) + +dnl ----------------------------------------------------------------- +# ----------------- DONE inttypes.h checks START header ------------- +AC_CONFIG_COMMANDS([$ac_stdint_h],[ +AC_MSG_NOTICE(creating $ac_stdint_h : $_ac_stdint_h) +ac_stdint=$tmp/_stdint.h + +echo "#ifndef" $_ac_stdint_h >$ac_stdint +echo "#define" $_ac_stdint_h "1" >>$ac_stdint +echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint +echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint +echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint +if test "_$ac_cv_header_stdint_t" != "_" ; then +echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint +echo "#include " >>$ac_stdint +echo "#endif" >>$ac_stdint +echo "#endif" >>$ac_stdint +else + +cat >>$ac_stdint < +#else +#include + +/* .................... configured part ............................ */ + +STDINT_EOF + +echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_header="$ac_cv_header_stdint_x" + echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint +fi + +echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_o" != "_" ; then + ac_header="$ac_cv_header_stdint_o" + echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint +fi + +echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint +if test "_$ac_cv_header_stdint_u" != "_" ; then + ac_header="$ac_cv_header_stdint_u" + echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint +fi + +echo "" >>$ac_stdint + +if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then + echo "#include <$ac_header>" >>$ac_stdint + echo "" >>$ac_stdint +fi fi + +echo "/* which 64bit typedef has been found */" >>$ac_stdint +if test "$ac_cv_type_uint64_t" = "yes" ; then +echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint +fi +if test "$ac_cv_type_u_int64_t" = "yes" ; then +echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* which type model has been detected */" >>$ac_stdint +if test "_$ac_cv_char_data_model" != "_" ; then +echo "#define _STDINT_CHAR_MODEL" "$ac_cv_char_data_model" >>$ac_stdint +echo "#define _STDINT_LONG_MODEL" "$ac_cv_long_data_model" >>$ac_stdint +else +echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint +echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* whether int_least types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_least32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint +fi +echo "/* whether int_fast types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_fast32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint +fi +echo "/* whether intmax_t type was detected */" >>$ac_stdint +if test "$ac_cv_type_intmax_t" = "yes"; then +echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + + cat >>$ac_stdint <= 199901L +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif !defined __STRICT_ANSI__ +#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ +#define _HAVE_UINT64_T +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ +/* note: all ELF-systems seem to have loff-support which needs 64-bit */ +#if !defined _NO_LONGLONG +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +#elif defined __alpha || (defined __mips && defined _ABIN32) +#if !defined _NO_LONGLONG +typedef long int64_t; +typedef unsigned long uint64_t; +#endif + /* compiler/cpu type to define int64_t */ +#endif +#endif +#endif + +#if defined _STDINT_HAVE_U_INT_TYPES +/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */ +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; + +/* glibc compatibility */ +#ifndef __int8_t_defined +#define __int8_t_defined +#endif +#endif + +#ifdef _STDINT_NEED_INT_MODEL_T +/* we must guess all the basic types. Apart from byte-adressable system, */ +/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */ +/* (btw, those nibble-addressable systems are way off, or so we assume) */ + +dnl /* have a look at "64bit and data size neutrality" at */ +dnl /* http://unix.org/version2/whatsnew/login_64bit.html */ +dnl /* (the shorthand "ILP" types always have a "P" part) */ + +#if defined _STDINT_BYTE_MODEL +#if _STDINT_LONG_MODEL+0 == 242 +/* 2:4:2 = IP16 = a normal 16-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef long int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444 +/* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */ +/* 4:4:4 = ILP32 = a normal 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488 +/* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */ +/* 4:8:8 = LP64 = a normal 64-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* this system has a "long" of 64bit */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef unsigned long uint64_t; +typedef long int64_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 448 +/* LLP64 a 64-bit system derived from a 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* assuming the system has a "long long" */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef unsigned long long uint64_t; +typedef long long int64_t; +#endif +#else +#define _STDINT_NO_INT32_T +#endif +#else +#define _STDINT_NO_INT8_T +#define _STDINT_NO_INT32_T +#endif +#endif + +/* + * quote from SunOS-5.8 sys/inttypes.h: + * Use at your own risk. As of February 1996, the committee is squarely + * behind the fixed sized types; the "least" and "fast" types are still being + * discussed. The probability that the "fast" types may be removed before + * the standard is finalized is high enough that they are not currently + * implemented. + */ + +#if defined _STDINT_NEED_INT_LEAST_T +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_least64_t; +#endif + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_least64_t; +#endif + /* least types */ +#endif + +#if defined _STDINT_NEED_INT_FAST_T +typedef int8_t int_fast8_t; +typedef int int_fast16_t; +typedef int32_t int_fast32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_fast64_t; +#endif + +typedef uint8_t uint_fast8_t; +typedef unsigned uint_fast16_t; +typedef uint32_t uint_fast32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_fast64_t; +#endif + /* fast types */ +#endif + +#ifdef _STDINT_NEED_INTMAX_T +#ifdef _HAVE_UINT64_T +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; +#else +typedef long intmax_t; +typedef unsigned long uintmax_t; +#endif +#endif + +#ifdef _STDINT_NEED_INTPTR_T +#ifndef __intptr_t_defined +#define __intptr_t_defined +/* we encourage using "long" to store pointer values, never use "int" ! */ +#if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484 +typedef unsigned int uintptr_t; +typedef int intptr_t; +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T +typedef uint64_t uintptr_t; +typedef int64_t intptr_t; +#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */ +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +#endif +#endif + +/* The ISO C99 standard specifies that in C++ implementations these + should only be defined if explicitly requested. */ +#if !defined __cplusplus || defined __STDC_CONSTANT_MACROS +#ifndef UINT32_C + +/* Signed. */ +# define INT8_C(c) c +# define INT16_C(c) c +# define INT32_C(c) c +# ifdef _HAVE_LONGLONG_UINT64_T +# define INT64_C(c) c ## L +# else +# define INT64_C(c) c ## LL +# endif + +/* Unsigned. */ +# define UINT8_C(c) c ## U +# define UINT16_C(c) c ## U +# define UINT32_C(c) c ## U +# ifdef _HAVE_LONGLONG_UINT64_T +# define UINT64_C(c) c ## UL +# else +# define UINT64_C(c) c ## ULL +# endif + +/* Maximal type. */ +# ifdef _HAVE_LONGLONG_UINT64_T +# define INTMAX_C(c) c ## L +# define UINTMAX_C(c) c ## UL +# else +# define INTMAX_C(c) c ## LL +# define UINTMAX_C(c) c ## ULL +# endif + + /* literalnumbers */ +#endif +#endif + +/* These limits are merily those of a two complement byte-oriented system */ + +/* Minimum of signed integral types. */ +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +# define INT64_MIN (-__INT64_C(9223372036854775807)-1) +/* Maximum of signed integral types. */ +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +# define INT64_MAX (__INT64_C(9223372036854775807)) + +/* Maximum of unsigned integral types. */ +# define UINT8_MAX (255) +# define UINT16_MAX (65535) +# define UINT32_MAX (4294967295U) +# define UINT64_MAX (__UINT64_C(18446744073709551615)) + +/* Minimum of signed integral types having a minimum size. */ +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# define INT_LEAST64_MIN INT64_MIN +/* Maximum of signed integral types having a minimum size. */ +# define INT_LEAST8_MAX INT8_MAX +# define INT_LEAST16_MAX INT16_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST64_MAX INT64_MAX + +/* Maximum of unsigned integral types having a minimum size. */ +# define UINT_LEAST8_MAX UINT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define UINT_LEAST64_MAX UINT64_MAX + + /* shortcircuit*/ +#endif + /* once */ +#endif +#endif +STDINT_EOF +fi + if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then + AC_MSG_NOTICE([$ac_stdint_h is unchanged]) + else + ac_dir=`AS_DIRNAME(["$ac_stdint_h"])` + AS_MKDIR_P(["$ac_dir"]) + rm -f $ac_stdint_h + mv $ac_stdint $ac_stdint_h + fi +],[# variables for create stdint.h replacement +PACKAGE="$PACKAGE" +VERSION="$VERSION" +ac_stdint_h="$ac_stdint_h" +_ac_stdint_h=AS_TR_CPP(_$PACKAGE-$ac_stdint_h) +ac_cv_stdint_message="$ac_cv_stdint_message" +ac_cv_header_stdint_t="$ac_cv_header_stdint_t" +ac_cv_header_stdint_x="$ac_cv_header_stdint_x" +ac_cv_header_stdint_o="$ac_cv_header_stdint_o" +ac_cv_header_stdint_u="$ac_cv_header_stdint_u" +ac_cv_type_uint64_t="$ac_cv_type_uint64_t" +ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t" +ac_cv_char_data_model="$ac_cv_char_data_model" +ac_cv_long_data_model="$ac_cv_long_data_model" +ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t" +ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t" +ac_cv_type_intmax_t="$ac_cv_type_intmax_t" +]) +]) diff --git a/config/config.guess b/config/config.guess new file mode 100755 index 0000000000..dff9e481b7 --- /dev/null +++ b/config/config.guess @@ -0,0 +1,1317 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-09-04' + +# This file 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Written by Per Bothner . +# Please send patches to . +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + + +dummy=dummy-$$ +trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int dummy(){}" > $dummy.c ; + for c in cc gcc c89 ; do + ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ; + if test $? = 0 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + rm -f $dummy.c $dummy.o $dummy.rel ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # Netbsd (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # Determine the machine/vendor (is the vendor relevant). + case "${UNAME_MACHINE}" in + amiga) machine=m68k-unknown ;; + arm32) machine=arm-unknown ;; + atari*) machine=m68k-atari ;; + sun3*) machine=m68k-sun ;; + mac68k) machine=m68k-apple ;; + macppc) machine=powerpc-apple ;; + hp3[0-9][05]) machine=m68k-hp ;; + ibmrt|romp-ibm) machine=romp-ibm ;; + *) machine=${UNAME_MACHINE}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE}" in + i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + cat <$dummy.s + .data +\$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + + .text + .globl main + .align 4 + .ent main +main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) + UNAME_MACHINE="alpha" + ;; + 1-0) + UNAME_MACHINE="alphaev5" + ;; + 1-1) + UNAME_MACHINE="alphaev56" + ;; + 1-101) + UNAME_MACHINE="alphapca56" + ;; + 2-303) + UNAME_MACHINE="alphaev6" + ;; + 2-307) + UNAME_MACHINE="alphaev67" + ;; + 2-1307) + UNAME_MACHINE="alphaev68" + ;; + esac + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + arc64:OpenBSD:*:*) + echo mips64el-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hkmips:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + sparc*:NetBSD:*) + echo `uname -p`-unknown-netbsd${UNAME_RELEASE} + exit 0 ;; + atari*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + sun3*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy \ + && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + case "${HPUX_REV}" in + 11.[0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + esac ;; + esac + fi ;; + esac + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy` + if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi + rm -f $dummy.c $dummy + fi ;; + esac + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + hppa*:OpenBSD:*:*) + echo hppa-unknown-openbsd + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3D:*:*:*) + echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i386-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in + big) echo mips-unknown-linux-gnu && exit 0 ;; + little) echo mipsel-unknown-linux-gnu && exit 0 ;; + esac + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + ld_supported_targets=`cd /; ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + cat >$dummy.c < +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __ELF__ +# ifdef __GLIBC__ +# if __GLIBC__ >= 2 + printf ("%s-pc-linux-gnu\n", argv[1]); +# else + printf ("%s-pc-linux-gnulibc1\n", argv[1]); +# endif +# else + printf ("%s-pc-linux-gnulibc1\n", argv[1]); +# endif +#else + printf ("%s-pc-linux-gnuaout\n", argv[1]); +#endif + return 0; +} +EOF + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + echo `uname -p`-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + if test "${UNAME_MACHINE}" = "x86pc"; then + UNAME_MACHINE=pc + fi + echo `uname -p`-${UNAME_MACHINE}-nto-qnx + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[KW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0 +rm -f $dummy.c $dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config/config.h.in b/config/config.h.in new file mode 100644 index 0000000000..a7fec1d2e8 --- /dev/null +++ b/config/config.h.in @@ -0,0 +1,203 @@ +/* config/config.h.in. Generated from configure.ac by autoheader. */ + + +#ifndef CONFIG_AUTO_H +#define CONFIG_AUTO_H +/* config_auto.h: begin */ + + +/* Define when compiling in debug mode */ +#undef DEBUG_MODE + +/* directory "bindir" */ +#undef DIR_BINDIR + +/* directory "datadir" */ +#undef DIR_DATADIR + +/* directory "exec_prefix" */ +#undef DIR_EXEC_PREFIX + +/* directory "libdir" */ +#undef DIR_LIBDIR + +/* directory "mandir" */ +#undef DIR_MANDIR + +/* directory "prefix" */ +#undef DIR_PREFIX + +/* Define to 1 if you have the `acos' function. */ +#undef HAVE_ACOS + +/* Define to 1 if you have the `asin' function. */ +#undef HAVE_ASIN + +/* define if bool is a built-in type */ +#undef HAVE_BOOL + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `gethostname' function. */ +#undef HAVE_GETHOSTNAME + +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define if you have the www.libtiff.org LIBTIFF library. */ +#undef HAVE_LIBTIFF + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if the system has the type `long long int'. */ +#undef HAVE_LONG_LONG_INT + +/* Define to 1 if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if the system has the type `mbstate_t'. */ +#undef HAVE_MBSTATE_T + +/* Define to 1 if you have the `memcpy' function. */ +#undef HAVE_MEMCPY + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define to 1 if stdbool.h conforms to C99. */ +#undef HAVE_STDBOOL_H + +/* define if the compiler comes with standard includes */ +#undef HAVE_STDINCLUDES + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IPC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SHM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* define if the compiler recognizes typename */ +#undef HAVE_TYPENAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if the system has the type `wchar_t'. */ +#undef HAVE_WCHAR_T + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Official date of release */ +#undef PACKAGE_DATE + +/* Name of package */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Version number */ +#undef PACKAGE_VERSION + +/* Official year for this release */ +#undef PACKAGE_YEAR + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Version number of package */ +#undef VERSION + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned' if does not define. */ +#undef size_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork + + + +/* Miscellaneous defines */ +#define AUTOCONF 1 + +/* config_auto.h: end */ +#endif + diff --git a/config/config.sub b/config/config.sub new file mode 100755 index 0000000000..393f13d373 --- /dev/null +++ b/config/config.sub @@ -0,0 +1,1411 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-09-07' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dsp16xx \ + | fr30 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips16 | mips64 | mips64el | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el | mips64vr4300 \ + | mips64vr4300el | mips64vr5000 | mips64vr5000el \ + | mipsbe | mipseb | mipsel | mipsle | mipstx39 | mipstx39el \ + | mipsisa32 \ + | mn10200 | mn10300 \ + | ns16k | ns32k \ + | openrisc \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | s390 | s390x \ + | sh | sh[34] | sh[34]eb | shbe | shle \ + | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \ + | stormy16 | strongarm \ + | tahoe | thumb | tic80 | tron \ + | v850 \ + | we32k \ + | x86 | xscale \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alphapca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armv*-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c54x-* \ + | clipper-* | cray2-* | cydra-* \ + | d10v-* | d30v-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | m32r-* \ + | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \ + | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipseb-* \ + | mipsle-* | mipsel-* | mipstx39-* | mipstx39el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | s390-* | s390x-* \ + | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | stormy16-* | strongarm-* | sv1-* \ + | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \ + | v850-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [cjt]90) + basic_machine=${basic_machine}-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mipsel*-linux*) + basic_machine=mipsel-unknown + os=-linux-gnu + ;; + mips*-linux*) + basic_machine=mips-unknown + os=-linux-gnu + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i686-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=t3e-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + windows32) + basic_machine=i386-pc + os=-windows32-msvcrt + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + mips) + if [ x$os = x-linux-gnu ]; then + basic_machine=mips-unknown + else + basic_machine=mips-mips + fi + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh3eb | sh4eb) + basic_machine=sh-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + c4x*) + basic_machine=c4x-none + os=-coff + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto*) + os=-nto-qnx + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config/depcomp b/config/depcomp new file mode 100755 index 0000000000..807b991f4a --- /dev/null +++ b/config/depcomp @@ -0,0 +1,423 @@ +#! /bin/sh + +# depcomp - compile a program generating dependencies as side-effects +# Copyright 1999, 2000 Free Software Foundation, Inc. + +# 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 2, 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi +# `libtool' can also be set to `yes' or `no'. + +if test -z "$depfile"; then + base=`echo "$object" | sed -e 's,^.*/,,' -e 's,\.\([^.]*\)$,.P\1,'` + dir=`echo "$object" | sed 's,/.*$,/,'` + if test "$dir" = "$object"; then + dir= + fi + # FIXME: should be _deps on DOS. + depfile="$dir.deps/$base" +fi + +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. This file always lives in the current directory. + # Also, the AIX compiler puts `$object:' at the start of each line; + # $object doesn't have directory information. + stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + outname="$stripped.o" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + tmpdepfile1="$dir.libs/$base.lo.d" + tmpdepfile2="$dir.libs/$base.d" + "$@" -Wc,-MD + else + tmpdepfile1="$dir$base.o.d" + tmpdepfile2="$dir$base.d" + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + if test -f "$tmpdepfile1"; then + tmpdepfile="$tmpdepfile1" + else + tmpdepfile="$tmpdepfile2" + fi + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a space and a tab in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. We will use -o /dev/null later, + # however we can't do the remplacement now because + # `-o $object' might simply not be used + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + "$@" -o /dev/null $dashmflag | sed 's:^[^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # X makedepend + shift + cleared=no + for arg in "$@"; do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + -*) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + "$@" || exit $? + IFS=" " + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 diff --git a/config/install-sh b/config/install-sh new file mode 100755 index 0000000000..11870f1b01 --- /dev/null +++ b/config/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + : +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=$mkdirprog + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f "$src" ] || [ -d "$src" ] + then + : + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + : + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + : + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' + ' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + : + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + : + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/config/missing b/config/missing new file mode 100755 index 0000000000..6a37006e8f --- /dev/null +++ b/config/missing @@ -0,0 +1,336 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright (C) 1996, 1997, 1999, 2000, 2002 Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# 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 2, 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing 0.4 - GNU automake" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. + You can get \`$1Help2man' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then + # We have makeinfo, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + tar) + shift + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + fi + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequirements for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 diff --git a/config/mkinstalldirs b/config/mkinstalldirs new file mode 100755 index 0000000000..8ab885ec92 --- /dev/null +++ b/config/mkinstalldirs @@ -0,0 +1,99 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +errstatus=0 +dirmode="" + +usage="\ +Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..." + +# process command line arguments +while test $# -gt 0 ; do + case "${1}" in + -h | --help | --h* ) # -h for help + echo "${usage}" 1>&2; exit 0 ;; + -m ) # -m PERM arg + shift + test $# -eq 0 && { echo "${usage}" 1>&2; exit 1; } + dirmode="${1}" + shift ;; + -- ) shift; break ;; # stop option processing + -* ) echo "${usage}" 1>&2; exit 1 ;; # unknown option + * ) break ;; # first non-opt arg + esac +done + +for file +do + if test -d "$file"; then + shift + else + break + fi +done + +case $# in +0) exit 0 ;; +esac + +case $dirmode in +'') + if mkdir -p -- . 2>/dev/null; then + echo "mkdir -p -- $*" + exec mkdir -p -- "$@" + fi ;; +*) + if mkdir -m "$dirmode" -p -- . 2>/dev/null; then + echo "mkdir -m $dirmode -p -- $*" + exec mkdir -m "$dirmode" -p -- "$@" + fi ;; +esac + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + else + if test ! -z "$dirmode"; then + echo "chmod $dirmode $pathcomp" + + lasterr="" + chmod "$dirmode" "$pathcomp" || lasterr=$? + + if test ! -z "$lasterr"; then + errstatus=$lasterr + fi + fi + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# Local Variables: +# mode: shell-script +# sh-indentation: 3 +# End: +# mkinstalldirs ends here diff --git a/configure b/configure new file mode 100755 index 0000000000..ab8f797916 --- /dev/null +++ b/configure @@ -0,0 +1,9541 @@ +#! /bin/sh +# From configure.ac Id: configure.ac. +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59 for Tesseract 1.03. +# +# Report bugs to . +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME='Tesseract' +PACKAGE_TARNAME='tesseract' +PACKAGE_VERSION='1.03' +PACKAGE_STRING='Tesseract 1.03' +PACKAGE_BUGREPORT='theraysmith@users.sourceforge.net' + +ac_unique_file="ccmain/tesseractmain.cpp" +ac_default_prefix=/usr/local +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os PACKAGE_YEAR PACKAGE_DATE CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CC CFLAGS ac_ct_CC CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT USING_CL_TRUE USING_CL_FALSE RANLIB ac_ct_RANLIB GNUWIN32_DIR HAVE_GNUWIN32_TRUE HAVE_GNUWIN32_FALSE OPTS CXXRPOFLAGS RPO_YES RPO_NO CXXCPP EGREP LIBTIFF_LIBS LIBTIFF_CFLAGS HAVE_LIBTIFF_TRUE HAVE_LIBTIFF_FALSE LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CXX_set=${CXX+set} +ac_env_CXX_value=$CXX +ac_cv_env_CXX_set=${CXX+set} +ac_cv_env_CXX_value=$CXX +ac_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_env_CXXFLAGS_value=$CXXFLAGS +ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_cv_env_CXXFLAGS_value=$CXXFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_GNUWIN32_DIR_set=${GNUWIN32_DIR+set} +ac_env_GNUWIN32_DIR_value=$GNUWIN32_DIR +ac_cv_env_GNUWIN32_DIR_set=${GNUWIN32_DIR+set} +ac_cv_env_GNUWIN32_DIR_value=$GNUWIN32_DIR +ac_env_CXXCPP_set=${CXXCPP+set} +ac_env_CXXCPP_value=$CXXCPP +ac_cv_env_CXXCPP_set=${CXXCPP+set} +ac_cv_env_CXXCPP_value=$CXXCPP +ac_env_LIBTIFF_LIBS_set=${LIBTIFF_LIBS+set} +ac_env_LIBTIFF_LIBS_value=$LIBTIFF_LIBS +ac_cv_env_LIBTIFF_LIBS_set=${LIBTIFF_LIBS+set} +ac_cv_env_LIBTIFF_LIBS_value=$LIBTIFF_LIBS +ac_env_LIBTIFF_CFLAGS_set=${LIBTIFF_CFLAGS+set} +ac_env_LIBTIFF_CFLAGS_value=$LIBTIFF_CFLAGS +ac_cv_env_LIBTIFF_CFLAGS_set=${LIBTIFF_CFLAGS+set} +ac_cv_env_LIBTIFF_CFLAGS_value=$LIBTIFF_CFLAGS + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures Tesseract 1.03 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of Tesseract 1.03:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors + --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer + --enable-debug Compile with debugging options (default: no) + --enable-rpo Enable compilation with option -frepo + --disable-largefile omit support for large files + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-extra-includes=DIR + Define an additional directory for include files + --with-extra-libraries=DIR + Define an additional directory for library files + --with-gnuwin32=DIR where the GnuWin32 packages are installed + --with-libtiff=DIR where the www.libtiff.org libtiff library is located + +Some influential environment variables: + CXX C++ compiler command + CXXFLAGS C++ compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CC C compiler command + CFLAGS C compiler flags + GNUWIN32_DIR + Base directory of GnuWin32 packages + CXXCPP C++ preprocessor + LIBTIFF_LIBS + Tiff library to link against + LIBTIFF_CFLAGS + Compile flags needed for TIFF support + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF +Tesseract configure 1.03 +generated by GNU Autoconf 2.59 + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by Tesseract $as_me 1.03, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + + + + + + + +tesseract + +ac_aux_dir= +for ac_dir in config $srcdir/config; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in config $srcdir/config" >&5 +echo "$as_me: error: cannot find install-sh or install.sh in config $srcdir/config" >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + + + +# Make sure we can run config.sub. +$ac_config_sub sun4 >/dev/null 2>&1 || + { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 +echo "$as_me: error: cannot run $ac_config_sub" >&2;} + { (exit 1); exit 1; }; } + +echo "$as_me:$LINENO: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6 +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_build_alias=$build_alias +test -z "$ac_cv_build_alias" && + ac_cv_build_alias=`$ac_config_guess` +test -z "$ac_cv_build_alias" && + { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6 +build=$ac_cv_build +build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6 +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_host_alias=$host_alias +test -z "$ac_cv_host_alias" && + ac_cv_host_alias=$ac_cv_build_alias +ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6 +host=$ac_cv_host +host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + + +# Define date of package, etc. Could be useful in auto-generated +# documentation. +# TODO(luc) Generate good documentation using doxygen or equivalent +PACKAGE_YEAR=2006 +PACKAGE_DATE="06/2006" + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "${PACKAGE_NAME}" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "${PACKAGE_VERSION}" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_YEAR "$PACKAGE_YEAR" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_DATE "$PACKAGE_DATE" +_ACEOF + + + + + + + + +# Check whether --with-extra-includes or --without-extra-includes was given. +if test "${with_extra_includes+set}" = set; then + withval="$with_extra_includes" + if test -d "$withval" ; then + CFLAGS="$CFLAGS -I$withval" + else + { { echo "$as_me:$LINENO: error: Cannot stat directory $withval" >&5 +echo "$as_me: error: Cannot stat directory $withval" >&2;} + { (exit 1); exit 1; }; } + fi +fi; + + +# Check whether --with-extra-libraries or --without-extra-libraries was given. +if test "${with_extra_libraries+set}" = set; then + withval="$with_extra_libraries" + if test -d "$withval" ; then + LDFLAGS="$LDFLAGS -L$withval" + else + { { echo "$as_me:$LINENO: error: Cannot stat directory $withval" >&5 +echo "$as_me: error: Cannot stat directory $withval" >&2;} + { (exit 1); exit 1; }; } + fi +fi; + +# Always look into a "gnu" directory. +curwd=`pwd` +if test -d $curwd/gnu/include ; then + CPPFLAGS="$CPPFLAGS -I$curwd/gnu/include" +fi +if test -d $curwd/gnu/lib ; then + LDFLAGS="$LDFLAGS -L$curwd/gnu/lib" +fi + +# Special cases +case "$host" in + *-darwin* | *-macos10*) + if test -d /opt/local ; then + CPPFLAGS="$CPPFLAGS -I/opt/local/include" + LDFLAGS="$LDFLAGS -L/opt/local/lib" + elif test -d /sw ; then + CPPFLAGS="$CPPFLAGS -I/sw/include" + LDFLAGS="$LDFLAGS -L/sw/lib" + fi + ;; +esac + +# ---------------------------------------- +# Check Compiler Characteristics and +# configure automake. The two appear to +# be intimately linked... +# ---------------------------------------- + +# Define order of compilers +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -n "$ac_tool_prefix"; then + for ac_prog in $CCC cl.exe g++ + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + echo "$as_me:$LINENO: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in $CCC cl.exe g++ +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 +echo "${ECHO_T}$ac_ct_CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CXX" && break +done +test -n "$ac_ct_CXX" || ac_ct_CXX="g++" + + CXX=$ac_ct_CXX +fi + + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C++ compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5 +echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C++ compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5 +echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 +GXX=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +CXXFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 +echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cxx_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cxx_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +# Not needed +# AC_PROG_CC + +# Automake configuration +# ---------------------------------------- + +# Note: may need to configure automake to use ZIP as a distribution +# format because of an apparent bug with GZIP, which results in bogus +# archives. +# TODO(luc) Resolve this issue. +#AM_INIT_AUTOMAKE(dist-zip) +am__api_version="1.9" +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6 +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { echo "$as_me:$LINENO: error: newly created file is older than distributed files! +Check your system clock" >&5 +echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +test "$program_prefix" != NONE && + program_transform_name="s,^,$program_prefix,;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$,$program_suffix,;$program_transform_name" +# Double any \ or $. echo might interpret backslashes. +# By default was `s,x,x', remove it if useless. +cat <<\_ACEOF >conftest.sed +s/[\\$]/&&/g;s/;s,x,x,$// +_ACEOF +program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +rm conftest.sed + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + echo "$as_me:$LINENO: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$AWK" && break +done + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +DEPDIR="${am__leading_dot}deps" + + ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6 +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi + + +echo "$as_me:$LINENO: result: $_am_result" >&5 +echo "${ECHO_T}$_am_result" >&6 +rm -f confinc confmf + +# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval="$enable_dependency_tracking" + +fi; +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + + +if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='tesseract' + VERSION='1.03' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +install_sh=${install_sh-"$am_aux_dir/install-sh"} + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + echo "$as_me:$LINENO: result: $STRIP" >&5 +echo "${ECHO_T}$STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":" +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +echo "${ECHO_T}$ac_ct_STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + STRIP=$ac_ct_STRIP +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. + +AMTAR=${AMTAR-"${am_missing_run}tar"} + +am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' + + + + +depcc="$CXX" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CXX_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CXX_dependencies_compiler_type" >&6 +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + + + ac_config_headers="$ac_config_headers config_auto.h:config/config.h.in" + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6 +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +if test "x$CC" != xcc; then + echo "$as_me:$LINENO: checking whether $CC and cc understand -c and -o together" >&5 +echo $ECHO_N "checking whether $CC and cc understand -c and -o together... $ECHO_C" >&6 +else + echo "$as_me:$LINENO: checking whether cc understands -c and -o together" >&5 +echo $ECHO_N "checking whether cc understands -c and -o together... $ECHO_C" >&6 +fi +set dummy $CC; ac_cc=`echo $2 | + sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +if eval "test \"\${ac_cv_prog_cc_${ac_cc}_c_o+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +# Make sure it works both with $CC and with simple cc. +# We do the test twice because some compilers refuse to overwrite an +# existing .o file with -o, though they will create one. +ac_try='$CC -c conftest.$ac_ext -o conftest.$ac_objext >&5' +if { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + test -f conftest.$ac_objext && { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; +then + eval ac_cv_prog_cc_${ac_cc}_c_o=yes + if test "x$CC" != xcc; then + # Test first that cc exists at all. + if { ac_try='cc -c conftest.$ac_ext >&5' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_try='cc -c conftest.$ac_ext -o conftest.$ac_objext >&5' + if { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + test -f conftest.$ac_objext && { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; + then + # cc works too. + : + else + # cc exists but doesn't like -o. + eval ac_cv_prog_cc_${ac_cc}_c_o=no + fi + fi + fi +else + eval ac_cv_prog_cc_${ac_cc}_c_o=no +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +cat >>confdefs.h <<\_ACEOF +#define NO_MINUS_C_MINUS_O 1 +_ACEOF + +fi + +# FIXME: we rely on the cache variable name because +# there is no other way. +set dummy $CC +ac_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" != yes"; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi + +echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6 + # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then + enableval="$enable_maintainer_mode" + USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi; + echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5 +echo "${ECHO_T}$USE_MAINTAINER_MODE" >&6 + + +if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + +# Need to tell automake if Visual C++ is being used: + + +if test x$CC = xcl.exe; then + USING_CL_TRUE= + USING_CL_FALSE='#' +else + USING_CL_TRUE='#' + USING_CL_FALSE= +fi + + +# Additional checking of compiler characteristics +# ---------------------------------------- + +# Check Endianness. If Big Endian, this will define WORDS_BIGENDIAN +# See also at end of this file, where we define INTEL_BYTE_ORDER +# or MOTOROLA_BYTE_ORDER. + +echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 +echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 +if test "${ac_cv_c_bigendian+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # See if sys/param.h defines the BYTE_ORDER macro. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It does; now see whether it defined to BIG_ENDIAN or not. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_bigendian=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +# It does not; compile a test program. +if test "$cross_compiling" = yes; then + # try to guess the endianness by grepping values into an object file + ac_cv_c_bigendian=unknown + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; +short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; +void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; } +short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; +short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; +void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; } +int +main () +{ + _ascii (); _ebcdic (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then + ac_cv_c_bigendian=yes +fi +if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi +fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int +main () +{ + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_bigendian=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 +echo "${ECHO_T}$ac_cv_c_bigendian" >&6 +case $ac_cv_c_bigendian in + yes) + +cat >>confdefs.h <<\_ACEOF +#define WORDS_BIGENDIAN 1 +_ACEOF + ;; + no) + ;; + *) + { { echo "$as_me:$LINENO: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&5 +echo "$as_me: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} + { (exit 1); exit 1; }; } ;; +esac + + + +# ---------------------------------------- +# Check for programs we need +# ---------------------------------------- + +# Check where all the following programs are and set +# variables accordingly: +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +# AC_PROG_LN_S +# AC_PATH_PROG(MV, mv) +# AC_PATH_PROG(CP, cp) +# AC_PATH_PROG(RM, rm) +# AC_PATH_PROG(AR, ar) +# AC_PATH_PROG(TOUCH, touch) +# AC_PATH_PROG(SED, sed) +# AC_PATH_PROG(BASH, bash, ,[$PATH:/usr/bin:/util/tools/bin]) +# # To use substitution in makefiles, use something like: +# AC_SUBST(BASH) + +# TODO(luc) Handle documentation. None of the following +# is really needed until then +# +# AC_PROG_DOXYGEN_VERSION(1.3.2,[DOXYGEN_OK=1]) +# AC_PATH_PROG(DOT, dot) +# AC_PATH_PROG(LATEX, latex) +# AC_PATH_PROG(DVIPS, dvips) +# AC_PATH_PROG(MAKEINDEX, makeindex) +# AC_PATH_PROG(PDFLATEX, pdflatex) +# AC_PATH_PROG(GZIP, gzip) +# +# if test -z "$DOXYGEN_OK" -o -z "$DOT"; then +# AC_MSG_WARN([------------------------------------ +# *** Disabling automatic documentation generation for this +# *** package. Please check that you have 'doxygen' (version +# *** $ac_doxygen_version or later) and 'graphviz' (aka, 'dot') +# *** installed on your system. In addition, to generate +# *** PostScript and PDF documentation, you will need to have +# *** LaTeX and PdfLaTeX respectively. Re-run this configuration +# *** script after you have updated your environment. +# --------------------------------------------------------]) +# +# # We have appropriate version of doxygen and dot, so we +# # can generate documentation. It remains to be seen whether +# # we can generate PDF and PostScript documentation.. +# else +# GENERATE_DOCUMENTATION="true" +# +# # Determine if PostScript documentation is generated: +# if test -z "$LATEX" -o -z "$DVIPS" -o -z "$MAKEINDEX"; then +# AC_MSG_WARN([Disabling generation of PostScript documentation]) +# else +# GENERATE_PS_DOCUMENTATION="true" +# fi +# +# # Determine if PDF documentation is generated: +# if test -z "$PDFLATEX" -o -z "$MAKEINDEX"; then +# AC_MSG_WARN([Disabling generation of PDF documentation]) +# else +# GENERATE_PDF_DOCUMENTATION="true" +# fi +# fi +# +# # These substitutions could be inside the 'else' +# # conditionals above, but it is not necessary and would +# # only cause some confusion... +# AC_SUBST(DOXYGEN) +# AC_SUBST(DOT) +# AC_SUBST(LATEX) +# AC_SUBST(DVIPS) +# AC_SUBST(MAKEINDEX) +# AC_SUBST(PDFLATEX) +# AC_SUBST(GZIP) +# +# # Adjust makefiles based on the kind of documentation that +# # is being generated, +# AM_CONDITIONAL(GENERATE_DOCUMENTATION, test -n "$GENERATE_DOCUMENTATION") +# AM_CONDITIONAL(GENERATE_PS_DOCUMENTATION, test -n "$GENERATE_PS_DOCUMENTATION") +# AM_CONDITIONAL(GENERATE_PDF_DOCUMENTATION, test -n "$GENERATE_PDF_DOCUMENTATION") + + +# Test for GNUWIN32 tools (only useful under windows) + + # Test whether we are running on Windows. This test is becoming + # useless because other environments exist under Windows, such + # as mingw, whihc gives host i686-pc-mingw32. Just remove this test + # for now by setting variable to yes all the time. +# AC_REQUIRE([AC_CANONICAL_HOST])[]dnl +# case $host_os in +# *cygwin* ) USING_CYGWIN_OR_MINGW=yes;; +# *mingw* ) USING_CYGWIN_OR_MINGW=yes;; +# * ) USING_CYGWIN_OR_MINGW=no;; +# esac + USING_CYGWIN_OR_MINGW=yes + # If running on Windows, do some tests + if test x$USING_CYGWIN_OR_MINGW = xyes ; then + + ac_gnuwin32=no + +# Check whether --with-gnuwin32 or --without-gnuwin32 was given. +if test "${with_gnuwin32+set}" = set; then + withval="$with_gnuwin32" + ac_gnuwin32=$withval +else + ac_gnuwin32=yes +fi; + # Process specification + echo "$as_me:$LINENO: checking for GnuWin32 directory" >&5 +echo $ECHO_N "checking for GnuWin32 directory... $ECHO_C" >&6 + if test x$ac_gnuwin32 = xyes ; then + # GNUWIN32_BASE could have been set on the command line + if test x$GNUWIN32_BASE != x ; then + if test -d "$GNUWIN32_BASE" ; then + echo "$as_me:$LINENO: result: verified at $GNUWIN32_BASE" >&5 +echo "${ECHO_T}verified at $GNUWIN32_BASE" >&6 + else + # AC_MSG_RESULT([no GnuWin32 at $GNUWIN32_BASE. Looking elsewhere]) + GNUWIN32_BASE= + fi + # Otherwise, GNUWIN32 is an environment variable that the user can set + elif test x$GNUWIN32 != x ; then + if test -d "$GNUWIN32" ; then + GNUWIN32_BASE=$GNUWIN32 + echo "$as_me:$LINENO: result: verified at $GNUWIN32_BASE" >&5 +echo "${ECHO_T}verified at $GNUWIN32_BASE" >&6 + else + # AC_MSG_RESULT([no GnuWin32 at $GNUWIN32. Looking elsewhere]) + GNUWIN32_BASE= + fi + fi + # Look in default locations if needed: + if test x$GNUWIN32_BASE = x ; then + if test -d "C:/Program Files/GnuWin32" ; then + GNUWIN32_BASE="C:/Program Files/GnuWin32" + echo "$as_me:$LINENO: result: $GNUWIN32_BASE" >&5 +echo "${ECHO_T}$GNUWIN32_BASE" >&6 + elif test -d "C:/pckg/GnuWin32" ; then + GNUWIN32_BASE="C:/pckg/GnuWin32" + echo "$as_me:$LINENO: result: $GNUWIN32_BASE" >&5 +echo "${ECHO_T}$GNUWIN32_BASE" >&6 + else + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + fi + fi + # If directory location specified on command line + elif test x$ac_gnuwin32 != xno ; then + if test -d "$ac_gnuwin32" ; then + GNUWIN32_BASE="$ac_gnuwin32" + echo "$as_me:$LINENO: result: verified at $GNUWIN32_BASE" >&5 +echo "${ECHO_T}verified at $GNUWIN32_BASE" >&6 + else + GNUWIN32_BASE= + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + fi + fi + # Now we can update LDFLAGS, CFLAGS and CXXFLAGS + # Do it in such a way that GnuWin32 has precedence + # over system includes and libraries + if test x$GNUWIN32_BASE != x ; then + CFLAGS="-I$GNUWIN32_BASE/include $CFLAGS" + CXXFLAGS="-I$GNUWIN32_BASE/include $CXXFLAGS" + if test x$CXX != xcl.exe ; then + LDFLAGS="-L$GNUWIN32_BASE/lib $LDFLAGS" + fi + fi + # If not running on cygwin, GNUWIN32 is useless + else + GNUWIN32_BASE= + fi + # Finally, sets automake conditional + + +if test x$GNUWIN32_BASE != x; then + HAVE_GNUWIN32_TRUE= + HAVE_GNUWIN32_FALSE='#' +else + HAVE_GNUWIN32_TRUE='#' + HAVE_GNUWIN32_FALSE= +fi + + + +# ---------------------------------------- +# C++ related options +# ---------------------------------------- + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +# Enable --enable-debug or --disable-debug and set +# compile options accordingly. We are supposed to be either +# in debug mode or in optimize mode. Note that in debug mode, +# DEBUG_MODE will be set by this macro + + + # Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + ac_debug=$enableval +else + ac_debug=no +fi; + OPTS= + + # Note: the /Za option might be nice to have, but it is + # incompatible with , which some packages + # (like the tiff library) require + VCOPTS_COMMON="/nologo /G5 /Zp1 /W3" + saved_CXXFLAGS="$CXXFLAGS" + saved_CFLAGS="$CFLAGS" + CXXFLAGS= + CFLAGS= + for opt in $saved_CXXFLAGS ; do + case $opt in + -g*) test $ac_debug != no && OPTS="$OPTS $opt" ;; + -O*) ;; + *) CXXFLAGS="$CXXFLAGS $opt" ;; + esac + done + for opt in $saved_CFLAGS ; do + case $opt in + -O*|-g*) ;; + *) CFLAGS="$CFLAGS $opt" ;; + esac + done + if test x$ac_debug = xno ; then + OPTS=-DNDEBUG + if test x$CXX = xcl.exe ; then + OPTS="$OPTS $VCOPTS_COMMON /Ow /O2" + else + + opt="-O3" + echo "$as_me:$LINENO: checking if $CXX accepts $opt" >&5 +echo $ECHO_N "checking if $CXX accepts $opt... $ECHO_C" >&6 + echo 'void f(){}' > conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + rm conftest.* + OPTS="$OPTS -O3" + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + rm conftest.* + + opt="-O2" + echo "$as_me:$LINENO: checking if $CXX accepts $opt" >&5 +echo $ECHO_N "checking if $CXX accepts $opt... $ECHO_C" >&6 + echo 'void f(){}' > conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + rm conftest.* + OPTS="$OPTS -O2" + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + rm conftest.* + + fi + + fi + + cpu=`uname -m 2>/dev/null` + test -z "$cpu" && cpu=${host_cpu} + case "${host_cpu}" in + i?86) + opt="-mcpu=${host_cpu}" + + opt="$opt" + echo "$as_me:$LINENO: checking if $CXX accepts $opt" >&5 +echo $ECHO_N "checking if $CXX accepts $opt... $ECHO_C" >&6 + echo 'void f(){}' > conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + rm conftest.* + OPTS="$OPTS $opt" + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + rm conftest.* + + fi + + ;; + esac + fi + else + if test x$CXX = xcl.exe ; then + OPTS="$OPTS $VCOPTS_COMMON /Od /Z7" + fi + +cat >>confdefs.h <<\_ACEOF +#define DEBUG_MODE 1 +_ACEOF + + fi + if test x$CXX != xcl.exe ; then + + opt="-Wall" + echo "$as_me:$LINENO: checking if $CXX accepts $opt" >&5 +echo $ECHO_N "checking if $CXX accepts $opt... $ECHO_C" >&6 + echo 'void f(){}' > conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + rm conftest.* + OPTS="$OPTS -Wall" + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + rm conftest.* + + fi + + fi + case x"$ac_debug" in + x[0-9]) OPTS="$OPTS -DDEBUGLVL=$ac_debug" ;; + xr*) OPTS="$OPTS -DRUNTIME_DEBUG_ONLY" ;; + esac + CXXFLAGS="$CXXFLAGS $OPTS" + CFLAGS="$CFLAGS $OPTS" + + + +echo "$as_me:$LINENO: checking whether the compiler recognizes bool as a built-in type" >&5 +echo $ECHO_N "checking whether the compiler recognizes bool as a built-in type... $ECHO_C" >&6 +if test "${ac_cv_cxx_bool+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int f(int x){return 1;} +int f(char x){return 1;} +int f(bool x){return 1;} + +int +main () +{ +bool b = true; return f(b); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_cxx_bool=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_cxx_bool=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_bool" >&5 +echo "${ECHO_T}$ac_cv_cxx_bool" >&6 +if test "$ac_cv_cxx_bool" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_BOOL 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether the compiler recognizes typename" >&5 +echo $ECHO_N "checking whether the compiler recognizes typename... $ECHO_C" >&6 +if test "${ac_cv_cxx_typename+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +templateclass X {public:X(){}}; +int +main () +{ +X z; return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_cxx_typename=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_cxx_typename=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_typename" >&5 +echo "${ECHO_T}$ac_cv_cxx_typename" >&6 +if test "$ac_cv_cxx_typename" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_TYPENAME 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether the compiler comes with standard includes" >&5 +echo $ECHO_N "checking whether the compiler comes with standard includes... $ECHO_C" >&6 +if test "${ac_cv_cxx_stdincludes+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +struct X { int a; X(int a):a(a){}; }; +X* foo(void *x) { return new(x) X(2); } +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_cxx_stdincludes=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_cxx_stdincludes=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_stdincludes" >&5 +echo "${ECHO_T}$ac_cv_cxx_stdincludes" >&6 +if test "$ac_cv_cxx_stdincludes" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STDINCLUDES 1 +_ACEOF + +fi + + CXXRPOFLAGS= + RPO_YES='#' + RPO_NO='' + if test x$GXX = xyes ; then + # Check whether --enable-rpo or --disable-rpo was given. +if test "${enable_rpo+set}" = set; then + enableval="$enable_rpo" + ac_rpo=$enableval +else + ac_rpo=no +fi; + if test x$ac_rpo != xno ; then + CXXRPOFLAGS='-frepo -fno-rtti' + RPO_YES='' + RPO_NO='#' + fi + fi + + + + + +# ---------------------------------------- +# Check for libraries +# ---------------------------------------- + +# This option seems to always add -lm to the link line, +# which causes unnecessary warnings with Visual C++. +# Comment it out for now. +#AC_CHECK_LIB(m,sqrt) + + +# ---------------------------------------- +# Checks for header files. +# ---------------------------------------- + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 +echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 +if test -z "$CXXCPP"; then + if test "${ac_cv_prog_CXXCPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +echo "$as_me:$LINENO: result: $CXXCPP" >&5 +echo "${ECHO_T}$CXXCPP" >&6 +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6 +if test "${ac_cv_header_sys_wait_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_sys_wait_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_sys_wait_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6 +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + +for ac_header in sys/ipc.h sys/shm.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------ ## +## Report this to theraysmith@users.sourceforge.net ## +## ------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_header in limits.h malloc.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------ ## +## Report this to theraysmith@users.sourceforge.net ## +## ------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +# Enable use of system-defined bool type if available: +echo "$as_me:$LINENO: checking for stdbool.h that conforms to C99" >&5 +echo $ECHO_N "checking for stdbool.h that conforms to C99... $ECHO_C" >&6 +if test "${ac_cv_header_stdbool_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#ifndef bool +# error bool is not defined +#endif +#ifndef false +# error false is not defined +#endif +#if false +# error false is not 0 +#endif +#ifndef true +# error true is not defined +#endif +#if true != 1 +# error true is not 1 +#endif +#ifndef __bool_true_false_are_defined +# error __bool_true_false_are_defined is not defined +#endif + + struct s { _Bool s: 1; _Bool t; } s; + + char a[true == 1 ? 1 : -1]; + char b[false == 0 ? 1 : -1]; + char c[__bool_true_false_are_defined == 1 ? 1 : -1]; + char d[(bool) -0.5 == true ? 1 : -1]; + bool e = &s; + char f[(_Bool) -0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + +int +main () +{ + return !a + !b + !c + !d + !e + !f + !g + !h + !i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdbool_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdbool_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdbool_h" >&5 +echo "${ECHO_T}$ac_cv_header_stdbool_h" >&6 +echo "$as_me:$LINENO: checking for _Bool" >&5 +echo $ECHO_N "checking for _Bool... $ECHO_C" >&6 +if test "${ac_cv_type__Bool+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((_Bool *) 0) + return 0; +if (sizeof (_Bool)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type__Bool=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type__Bool=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type__Bool" >&5 +echo "${ECHO_T}$ac_cv_type__Bool" >&6 +if test $ac_cv_type__Bool = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE__BOOL 1 +_ACEOF + + +fi + +if test $ac_cv_header_stdbool_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STDBOOL_H 1 +_ACEOF + +fi + + +# Misc +echo "$as_me:$LINENO: checking whether #! works in shell scripts" >&5 +echo $ECHO_N "checking whether #! works in shell scripts... $ECHO_C" >&6 +if test "${ac_cv_sys_interpreter+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + echo '#! /bin/cat +exit 69 +' >conftest +chmod u+x conftest +(SHELL=/bin/sh; export SHELL; ./conftest >/dev/null) +if test $? -ne 69; then + ac_cv_sys_interpreter=yes +else + ac_cv_sys_interpreter=no +fi +rm -f conftest +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_interpreter" >&5 +echo "${ECHO_T}$ac_cv_sys_interpreter" >&6 +interpval=$ac_cv_sys_interpreter + +# Check whether --enable-largefile or --disable-largefile was given. +if test "${enable_largefile+set}" = set; then + enableval="$enable_largefile" + +fi; +if test "$enable_largefile" != no; then + + echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5 +echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_largefile_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext + CC="$CC -n32" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_largefile_CC=' -n32'; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5 +echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6 + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_file_offset_bits+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + while :; do + ac_cv_sys_file_offset_bits=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_file_offset_bits=64; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + break +done +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5 +echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6 +if test "$ac_cv_sys_file_offset_bits" != no; then + +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF + +fi +rm -f conftest* + echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5 +echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_large_files+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + while :; do + ac_cv_sys_large_files=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_large_files=1; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + break +done +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5 +echo "${ECHO_T}$ac_cv_sys_large_files" >&6 +if test "$ac_cv_sys_large_files" != no; then + +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF + +fi +rm -f conftest* +fi + + + +# ---------------------------------------- +# Checks for typedefs, structures, and compiler characteristics. +# ---------------------------------------- + +echo "$as_me:$LINENO: checking for wchar_t" >&5 +echo $ECHO_N "checking for wchar_t... $ECHO_C" >&6 +if test "${ac_cv_type_wchar_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((wchar_t *) 0) + return 0; +if (sizeof (wchar_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_wchar_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_wchar_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_wchar_t" >&5 +echo "${ECHO_T}$ac_cv_type_wchar_t" >&6 +if test $ac_cv_type_wchar_t = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_WCHAR_T 1 +_ACEOF + + +fi + +echo "$as_me:$LINENO: checking for long long int" >&5 +echo $ECHO_N "checking for long long int... $ECHO_C" >&6 +if test "${ac_cv_type_long_long_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((long long int *) 0) + return 0; +if (sizeof (long long int)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long_long_int=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_long_long_int=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_long_long_int" >&5 +echo "${ECHO_T}$ac_cv_type_long_long_int" >&6 +if test $ac_cv_type_long_long_int = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_LONG_LONG_INT 1 +_ACEOF + + +fi + +echo "$as_me:$LINENO: checking for mbstate_t" >&5 +echo $ECHO_N "checking for mbstate_t... $ECHO_C" >&6 +if test "${ac_cv_type_mbstate_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include "wchar.h" + +int +main () +{ +if ((mbstate_t *) 0) + return 0; +if (sizeof (mbstate_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_mbstate_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_mbstate_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_mbstate_t" >&5 +echo "${ECHO_T}$ac_cv_type_mbstate_t" >&6 +if test $ac_cv_type_mbstate_t = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_MBSTATE_T 1 +_ACEOF + + +fi + + +#AC_TYPE_MODE_T +#AC_TYPE_OFF_T +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + +#AC_TYPE_PID_T + + +# ---------------------------------------- +# Checks for library functions. +# ---------------------------------------- + + + +for ac_header in stdlib.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------ ## +## Report this to theraysmith@users.sourceforge.net ## +## ------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_func in getpagesize +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +echo "$as_me:$LINENO: checking for working mmap" >&5 +echo $ECHO_N "checking for working mmap... $ECHO_C" >&6 +if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_mmap_fixed_mapped=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +/* malloc might have been renamed as rpl_malloc. */ +#undef malloc + +/* Thanks to Mike Haertel and Jim Avera for this test. + Here is a matrix of mmap possibilities: + mmap private not fixed + mmap private fixed at somewhere currently unmapped + mmap private fixed at somewhere already mapped + mmap shared not fixed + mmap shared fixed at somewhere currently unmapped + mmap shared fixed at somewhere already mapped + For private mappings, we should verify that changes cannot be read() + back from the file, nor mmap's back from the file at a different + address. (There have been systems where private was not correctly + implemented like the infamous i386 svr4.0, and systems where the + VM page cache was not coherent with the file system buffer cache + like early versions of FreeBSD and possibly contemporary NetBSD.) + For shared mappings, we should conversely verify that changes get + propagated back to all the places they're supposed to be. + + Grep wants private fixed already mapped. + The main things grep needs to know about mmap are: + * does it exist and is it safe to write into the mmap'd area + * how to use it (BSD variants) */ + +#include +#include + +#if !STDC_HEADERS && !HAVE_STDLIB_H +char *malloc (); +#endif + +/* This mess was copied from the GNU getpagesize.h. */ +#if !HAVE_GETPAGESIZE +/* Assume that all systems that can run configure have sys/param.h. */ +# if !HAVE_SYS_PARAM_H +# define HAVE_SYS_PARAM_H 1 +# endif + +# ifdef _SC_PAGESIZE +# define getpagesize() sysconf(_SC_PAGESIZE) +# else /* no _SC_PAGESIZE */ +# if HAVE_SYS_PARAM_H +# include +# ifdef EXEC_PAGESIZE +# define getpagesize() EXEC_PAGESIZE +# else /* no EXEC_PAGESIZE */ +# ifdef NBPG +# define getpagesize() NBPG * CLSIZE +# ifndef CLSIZE +# define CLSIZE 1 +# endif /* no CLSIZE */ +# else /* no NBPG */ +# ifdef NBPC +# define getpagesize() NBPC +# else /* no NBPC */ +# ifdef PAGESIZE +# define getpagesize() PAGESIZE +# endif /* PAGESIZE */ +# endif /* no NBPC */ +# endif /* no NBPG */ +# endif /* no EXEC_PAGESIZE */ +# else /* no HAVE_SYS_PARAM_H */ +# define getpagesize() 8192 /* punt totally */ +# endif /* no HAVE_SYS_PARAM_H */ +# endif /* no _SC_PAGESIZE */ + +#endif /* no HAVE_GETPAGESIZE */ + +int +main () +{ + char *data, *data2, *data3; + int i, pagesize; + int fd; + + pagesize = getpagesize (); + + /* First, make a file with some known garbage in it. */ + data = (char *) malloc (pagesize); + if (!data) + exit (1); + for (i = 0; i < pagesize; ++i) + *(data + i) = rand (); + umask (0); + fd = creat ("conftest.mmap", 0600); + if (fd < 0) + exit (1); + if (write (fd, data, pagesize) != pagesize) + exit (1); + close (fd); + + /* Next, try to mmap the file at a fixed address which already has + something else allocated at it. If we can, also make sure that + we see the same garbage. */ + fd = open ("conftest.mmap", O_RDWR); + if (fd < 0) + exit (1); + data2 = (char *) malloc (2 * pagesize); + if (!data2) + exit (1); + data2 += (pagesize - ((long) data2 & (pagesize - 1))) & (pagesize - 1); + if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0L)) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data2 + i)) + exit (1); + + /* Finally, make sure that changes to the mapped area do not + percolate back to the file as seen by read(). (This is a bug on + some variants of i386 svr4.0.) */ + for (i = 0; i < pagesize; ++i) + *(data2 + i) = *(data2 + i) + 1; + data3 = (char *) malloc (pagesize); + if (!data3) + exit (1); + if (read (fd, data3, pagesize) != pagesize) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data3 + i)) + exit (1); + close (fd); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_mmap_fixed_mapped=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_mmap_fixed_mapped=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5 +echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6 +if test $ac_cv_func_mmap_fixed_mapped = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MMAP 1 +_ACEOF + +fi +rm -f conftest.mmap + +echo "$as_me:$LINENO: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((pid_t *) 0) + return 0; +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pid_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6 +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + + + +for ac_header in unistd.h vfork.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------ ## +## Report this to theraysmith@users.sourceforge.net ## +## ------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_func in fork vfork +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + echo "$as_me:$LINENO: checking for working fork" >&5 +echo $ECHO_N "checking for working fork... $ECHO_C" >&6 +if test "${ac_cv_func_fork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_fork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* By Ruediger Kuhlmann. */ + #include + #if HAVE_UNISTD_H + # include + #endif + /* Some systems only have a dummy stub for fork() */ + int main () + { + if (fork() < 0) + exit (1); + exit (0); + } +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_fork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 +echo "${ECHO_T}$ac_cv_func_fork_works" >&6 + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + echo "$as_me:$LINENO: checking for working vfork" >&5 +echo $ECHO_N "checking for working vfork... $ECHO_C" >&6 +if test "${ac_cv_func_vfork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_vfork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +# include +#endif +#if HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + exit( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_vfork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_vfork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 +echo "${ECHO_T}$ac_cv_func_vfork_works" >&6 + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_VFORK 1 +_ACEOF + +else + +cat >>confdefs.h <<\_ACEOF +#define vfork fork +_ACEOF + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_FORK 1 +_ACEOF + +fi + + + +for ac_func in strerror vsnprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in gethostname +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + +for ac_func in strchr memcpy +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + +for ac_func in acos asin +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# ---------------------------------------- +# Test auxilliary packages +# ---------------------------------------- + +# Search JPEG library - not needed at the moment +# AC_PATH_JPEG(, +# [ no_jpeg=yes +# AC_MSG_WARN([JPEG support is disabled]) ]) + +# Search LIBTIFF library + + + case $host in + *msdos* | *go32* | *mingw32* | *cygwin* | *windows*) + USING_WIN=yes + ;; + *) + USING_WIN=no + esac + + + + ac_libtiff=no + +# Check whether --with-libtiff or --without-libtiff was given. +if test "${with_libtiff+set}" = set; then + withval="$with_libtiff" + ac_libtiff=$withval +else + ac_libtiff=yes +fi; + echo "$as_me:$LINENO: checking for Leffler libtiff library" >&5 +echo $ECHO_N "checking for Leffler libtiff library... $ECHO_C" >&6 + # Need to define TOP_SRCDIR for the cases where the LIBTIFF library + # is checked in with the code, at the top-level + TOP_SRCDIR=`cd $srcdir; pwd` + LOCAL_LIBTIFFDIR=$TOP_SRCDIR/libtiff + # First of all, deal with the case when 'cl.exe' is used as compiler + # indeed, when this is the case, we can only rely on finding + # the right library and includes, but we can't try compiling + # and linking. In addition, library needs to be specifically + # supplied on the link line and special flags are needed... + if test "x$CXX" = "xcl.exe" ; then + if test "x$ac_libtiff" = "xyes" ; then + # If LIBTIFF_LIBS has been set on configure command line + # or as environment variable, just use it if it exists + if test "x$LIBTIFF_LIBS" != "x" ; then + echo "$as_me:$LINENO: result: user specified as $LIBTIFF_LIBS" >&5 +echo "${ECHO_T}user specified as $LIBTIFF_LIBS" >&6 + if test "x$LIBTIFF_CFLAGS" = "x" ; then + { echo "$as_me:$LINENO: WARNING: LIBTIFF_CFLAGS is empty" >&5 +echo "$as_me: WARNING: LIBTIFF_CFLAGS is empty" >&2;} + fi + fi + # Otherwise, test if libtiff is at top level directory, under libtiff + if test "x$LIBTIFF_LIBS" = "x" && test -r "$LOCAL_LIBTIFFDIR/lib/libtiff.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libjpeg.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libz.a" ; then + echo "$as_me:$LINENO: result: $LOCAL_LIBTIFFDIR/lib/libtiff.a" >&5 +echo "${ECHO_T}$LOCAL_LIBTIFFDIR/lib/libtiff.a" >&6 + LIBTIFF_LIBS="$LOCAL_LIBTIFFDIR/lib/libtiff.a $LOCAL_LIBTIFFDIR/lib/libjpeg.a $LOCAL_LIBTIFFDIR/lib/libz.a user32.lib" + LIBTIFF_CFLAGS="-I$LOCAL_LIBTIFFDIR/include" + fi + # Otherwise, if GnuWin32 was previously located + if test "x$LIBTIFF_LIBS" = "x" && test -r "$GNUWIN32_BASE/lib/libtiff.a" && test -r "$GNUWIN32_BASE/lib/libjpeg.a" && test -r "$GNUWIN32_BASE/lib/libz.a" ; then + echo "$as_me:$LINENO: result: $GNUWIN32_BASE/lib/libtiff.a" >&5 +echo "${ECHO_T}$GNUWIN32_BASE/lib/libtiff.a" >&6 + { echo "$as_me:$LINENO: WARNING: $GNUWIN32_BASE/lib/libtiff.a version 3.5.7 works. Some versions like 3.6.1 do not!" >&5 +echo "$as_me: WARNING: $GNUWIN32_BASE/lib/libtiff.a version 3.5.7 works. Some versions like 3.6.1 do not!" >&2;} + LIBTIFF_LIBS="$GNUWIN32_BASE/lib/libtiff.a $GNUWIN32_BASE/lib/libjpeg.a $GNUWIN32_BASE/lib/libz.a user32.lib" + fi + if test "x$LIBTIFF_LIBS" = "x" ; then + echo "$as_me:$LINENO: result: not found or incomplete" >&5 +echo "${ECHO_T}not found or incomplete" >&6 + ac_libtiff=no + fi + elif test "x$ac_libtiff" != "xno" ; then + test x${LIBTIFF_LIBS+set} != xset && LIBTIFF_LIBS="$ac_libtiff/libtiff" + test x${LIBTIFF_CFLAGS+set} != xset && LIBTIFF_CFLAGS="-I$ac_libtiff" + fi + # If we are not using CL, that is we are either using gcc/cygwin + # or we are not running under Windows: + else + # Process specification + if test "x$ac_libtiff" = "xyes" ; then + if test "x$LIBTIFF_LIBS" = "x" ; then + # If local libtiff exists at top level, and we are running windows, use it first: + if test "x$USING_WIN" = "xyes" && test -r "$LOCAL_LIBTIFFDIR/lib/libtiff.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libjpeg.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libz.a" ; then + echo "$as_me:$LINENO: result: $LOCAL_LIBTIFFDIR/lib/libtiff.a" >&5 +echo "${ECHO_T}$LOCAL_LIBTIFFDIR/lib/libtiff.a" >&6 + LIBTIFF_LIBS="$LOCAL_LIBTIFFDIR/lib/libtiff.a $LOCAL_LIBTIFFDIR/lib/libjpeg.a $LOCAL_LIBTIFFDIR/lib/libz.a -lole32 -luuid -lwsock32" + LIBTIFF_CFLAGS="-I$LOCAL_LIBTIFFDIR/include" + fi + # If GNUWIN32_BASE is defined, it means we are running under + # Windows and we should use these builds if available because + # they are the best bet + if test "x$LIBTIFF_LIBS" = "x" && test "x$GNUWIN32_BASE" != "x" ; then + # With version 3.5.7 of gnuwin32 libtiff, we do not need to link + # against "$GNUWIN32_BASE/lib/libgw32c.a", so no need to test + if test -r "$GNUWIN32_BASE/lib/libtiff.a" && test -r "$GNUWIN32_BASE/lib/libjpeg.a" && test -r "$GNUWIN32_BASE/lib/libz.a" ; then + echo "$as_me:$LINENO: result: $GNUWIN32_BASE/lib/libtiff.a" >&5 +echo "${ECHO_T}$GNUWIN32_BASE/lib/libtiff.a" >&6 + # With 3.5.7 version of libtiff, no need to add + # $GNUWIN32_BASE/lib/libgw32c.a after $GNUWIN32_BASE/lib/libz.a + LIBTIFF_LIBS="$GNUWIN32_BASE/lib/libtiff.a $GNUWIN32_BASE/lib/libjpeg.a $GNUWIN32_BASE/lib/libz.a -lole32 -luuid -lwsock32" + else + echo "$as_me:$LINENO: result: GNUWIN32 not found or incomplete. Trying something else" >&5 +echo "${ECHO_T}GNUWIN32 not found or incomplete. Trying something else" >&6 + fi + fi + # If LIBTIFF_LIBS is still not defined after potentially going + # the GNUWIN32 route, then try using home built versions + if test "x$LIBTIFF_LIBS" = "x" ; then + TIFF_PACKAGE="$HOME_UNIX/packages/libtiff/libtiff" + if test -d "$TIFF_PACKAGE" ; then + LIBTIFF_LIBS="$TIFF_PACKAGE/libtiff.a" + LIBTIFF_CFLAGS="-I$TIFF_PACKAGE" + else + LIBTIFF_LIBS="-ltiff" + fi + fi + fi + elif test "x$ac_libtiff" != "xno" ; then + test "x${LIBTIFF_LIBS+set}" != "xset" && LIBTIFF_LIBS="$ac_libtiff/libtiff.a" + test "x${LIBTIFF_CFLAGS+set}" != "xset" && LIBTIFF_CFLAGS="-I$ac_libtiff" + fi + # Try linking - recall that -Wall is typically on, and any warning + # will cause this test to fail... This, by the way, is one of the + # reasons we cannot run this test when using the Microsoft compiler (cl), + # which outputs tons of "garbage" on stdout: + if test "x$ac_libtiff" != "xno" ; then + echo "$as_me:$LINENO: checking linking with $LIBTIFF_LIBS" >&5 +echo $ECHO_N "checking linking with $LIBTIFF_LIBS... $ECHO_C" >&6 + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LIBS="$LIBS" + CFLAGS="$CFLAGS $LIBTIFF_CFLAGS" + CXXFLAGS="$CXXFLAGS $LIBTIFF_CFLAGS" + LIBS="$LIBS $LIBTIFF_LIBS -lm" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif +int +main () +{ + +TIFFClose((TIFF *) 0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_libtiff=ok +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_libtiff=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + LIBS="$save_LIBS" + echo "$as_me:$LINENO: result: $ac_libtiff" >&5 +echo "${ECHO_T}$ac_libtiff" >&6 + fi + fi + # Finish + if test "x$ac_libtiff" = "xno"; then + LIBTIFF_CFLAGS= ; LIBTIFF_LIBS= + no_libtiff=yes + { echo "$as_me:$LINENO: WARNING: TIFF support is disabled" >&5 +echo "$as_me: WARNING: TIFF support is disabled" >&2;} + else + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBTIFF 1 +_ACEOF + + echo "$as_me:$LINENO: result: setting LIBTIFF_CFLAGS=$LIBTIFF_CFLAGS" >&5 +echo "${ECHO_T}setting LIBTIFF_CFLAGS=$LIBTIFF_CFLAGS" >&6 + echo "$as_me:$LINENO: result: setting LIBTIFF_LIBS=$LIBTIFF_LIBS" >&5 +echo "${ECHO_T}setting LIBTIFF_LIBS=$LIBTIFF_LIBS" >&6 + CFLAGS="$LIBTIFF_CFLAGS $CFLAGS" + CXXFLAGS="$LIBTIFF_CFLAGS $CXXFLAGS" + if test "x$CXX" = "xcl.exe" ; then + LIBS="$LIBTIFF_LIBS $LIBS" + else + LIBS="$LIBTIFF_LIBS $LIBS -lm" + fi + : + fi + + +if test x$ac_libtiff != xno; then + HAVE_LIBTIFF_TRUE= + HAVE_LIBTIFF_FALSE='#' +else + HAVE_LIBTIFF_TRUE='#' + HAVE_LIBTIFF_FALSE= +fi + + + + +# ---------------------------------------- +# Final Tasks and Output +# ---------------------------------------- + +# Define installation paths + + save_prefix="${prefix}" + save_exec_prefix="${exec_prefix}" + test "x$prefix" = xNONE && prefix="$ac_default_prefix" + test "x$exec_prefix" = xNONE && exec_prefix="$prefix" + DIR_PREFIX="`eval echo \"$prefix\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_PREFIX "${DIR_PREFIX}" +_ACEOF + + DIR_EXEC_PREFIX="`eval echo \"$exec_prefix\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_EXEC_PREFIX "${DIR_EXEC_PREFIX}" +_ACEOF + + DIR_BINDIR="`eval echo \"$bindir\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_BINDIR "${DIR_BINDIR}" +_ACEOF + + DIR_LIBDIR="`eval echo \"$libdir\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_LIBDIR "${DIR_LIBDIR}" +_ACEOF + + DIR_DATADIR="`eval echo \"$datadir\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_DATADIR "${DIR_DATADIR}" +_ACEOF + + DIR_MANDIR="`eval echo \"$mandir\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_MANDIR "${DIR_MANDIR}" +_ACEOF + + prefix="${save_prefix}" + exec_prefix="${save_exec_prefix}" + +# Redundant with PACKAGE_VERSION - comment out +# AC_DEFINE_UNQUOTED(TESSERACT_VERSION,["${PACKAGE_VERSION}"],[version string]) + +# Output files + ac_config_files="$ac_config_files Makefile" + + ac_config_files="$ac_config_files ccmain/Makefile" + + ac_config_files="$ac_config_files ccstruct/Makefile" + + ac_config_files="$ac_config_files ccutil/Makefile" + + ac_config_files="$ac_config_files classify/Makefile" + + ac_config_files="$ac_config_files cutil/Makefile" + + ac_config_files="$ac_config_files dict/Makefile" + + ac_config_files="$ac_config_files display/Makefile" + + ac_config_files="$ac_config_files image/Makefile" + + ac_config_files="$ac_config_files textord/Makefile" + + ac_config_files="$ac_config_files viewer/Makefile" + + ac_config_files="$ac_config_files wordrec/Makefile" + + ac_config_files="$ac_config_files training/Makefile" + +# AC_CONFIG_FILES(doc/Doxyfile) +# AC_CONFIG_FILES(doc/header.html) +# AC_CONFIG_FILES(doc/footer.html) +# AC_CONFIG_FILES(doc/header.tex) +# AC_CONFIG_FILES(doc/RTF_ExtensionFile) +# AC_CONFIG_FILES(doc/Makefile) +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${USING_CL_TRUE}" && test -z "${USING_CL_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"USING_CL\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"USING_CL\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${HAVE_GNUWIN32_TRUE}" && test -z "${HAVE_GNUWIN32_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"HAVE_GNUWIN32\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"HAVE_GNUWIN32\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${HAVE_LIBTIFF_TRUE}" && test -z "${HAVE_LIBTIFF_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"HAVE_LIBTIFF\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"HAVE_LIBTIFF\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by Tesseract $as_me 1.03, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +Tesseract config.status 1.03 +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# +# INIT-COMMANDS section. +# + +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "ccmain/Makefile" ) CONFIG_FILES="$CONFIG_FILES ccmain/Makefile" ;; + "ccstruct/Makefile" ) CONFIG_FILES="$CONFIG_FILES ccstruct/Makefile" ;; + "ccutil/Makefile" ) CONFIG_FILES="$CONFIG_FILES ccutil/Makefile" ;; + "classify/Makefile" ) CONFIG_FILES="$CONFIG_FILES classify/Makefile" ;; + "cutil/Makefile" ) CONFIG_FILES="$CONFIG_FILES cutil/Makefile" ;; + "dict/Makefile" ) CONFIG_FILES="$CONFIG_FILES dict/Makefile" ;; + "display/Makefile" ) CONFIG_FILES="$CONFIG_FILES display/Makefile" ;; + "image/Makefile" ) CONFIG_FILES="$CONFIG_FILES image/Makefile" ;; + "textord/Makefile" ) CONFIG_FILES="$CONFIG_FILES textord/Makefile" ;; + "viewer/Makefile" ) CONFIG_FILES="$CONFIG_FILES viewer/Makefile" ;; + "wordrec/Makefile" ) CONFIG_FILES="$CONFIG_FILES wordrec/Makefile" ;; + "training/Makefile" ) CONFIG_FILES="$CONFIG_FILES training/Makefile" ;; + "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "config_auto.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config_auto.h:config/config.h.in" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@build@,$build,;t t +s,@build_cpu@,$build_cpu,;t t +s,@build_vendor@,$build_vendor,;t t +s,@build_os@,$build_os,;t t +s,@host@,$host,;t t +s,@host_cpu@,$host_cpu,;t t +s,@host_vendor@,$host_vendor,;t t +s,@host_os@,$host_os,;t t +s,@PACKAGE_YEAR@,$PACKAGE_YEAR,;t t +s,@PACKAGE_DATE@,$PACKAGE_DATE,;t t +s,@CXX@,$CXX,;t t +s,@CXXFLAGS@,$CXXFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CXX@,$ac_ct_CXX,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@CYGPATH_W@,$CYGPATH_W,;t t +s,@PACKAGE@,$PACKAGE,;t t +s,@VERSION@,$VERSION,;t t +s,@ACLOCAL@,$ACLOCAL,;t t +s,@AUTOCONF@,$AUTOCONF,;t t +s,@AUTOMAKE@,$AUTOMAKE,;t t +s,@AUTOHEADER@,$AUTOHEADER,;t t +s,@MAKEINFO@,$MAKEINFO,;t t +s,@install_sh@,$install_sh,;t t +s,@STRIP@,$STRIP,;t t +s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t +s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t +s,@mkdir_p@,$mkdir_p,;t t +s,@AWK@,$AWK,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@am__leading_dot@,$am__leading_dot,;t t +s,@AMTAR@,$AMTAR,;t t +s,@am__tar@,$am__tar,;t t +s,@am__untar@,$am__untar,;t t +s,@DEPDIR@,$DEPDIR,;t t +s,@am__include@,$am__include,;t t +s,@am__quote@,$am__quote,;t t +s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t +s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t +s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t +s,@CXXDEPMODE@,$CXXDEPMODE,;t t +s,@am__fastdepCXX_TRUE@,$am__fastdepCXX_TRUE,;t t +s,@am__fastdepCXX_FALSE@,$am__fastdepCXX_FALSE,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@CCDEPMODE@,$CCDEPMODE,;t t +s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t +s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t +s,@MAINTAINER_MODE_TRUE@,$MAINTAINER_MODE_TRUE,;t t +s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t +s,@MAINT@,$MAINT,;t t +s,@USING_CL_TRUE@,$USING_CL_TRUE,;t t +s,@USING_CL_FALSE@,$USING_CL_FALSE,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@GNUWIN32_DIR@,$GNUWIN32_DIR,;t t +s,@HAVE_GNUWIN32_TRUE@,$HAVE_GNUWIN32_TRUE,;t t +s,@HAVE_GNUWIN32_FALSE@,$HAVE_GNUWIN32_FALSE,;t t +s,@OPTS@,$OPTS,;t t +s,@CXXRPOFLAGS@,$CXXRPOFLAGS,;t t +s,@RPO_YES@,$RPO_YES,;t t +s,@RPO_NO@,$RPO_NO,;t t +s,@CXXCPP@,$CXXCPP,;t t +s,@EGREP@,$EGREP,;t t +s,@LIBTIFF_LIBS@,$LIBTIFF_LIBS,;t t +s,@LIBTIFF_CFLAGS@,$LIBTIFF_CFLAGS,;t t +s,@HAVE_LIBTIFF_TRUE@,$HAVE_LIBTIFF_TRUE,;t t +s,@HAVE_LIBTIFF_FALSE@,$HAVE_LIBTIFF_FALSE,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +# Compute $ac_file's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $ac_file | $ac_file:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null || +$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X$ac_file : 'X\(//\)[^/]' \| \ + X$ac_file : 'X\(//\)$' \| \ + X$ac_file : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X$ac_file | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'`/stamp-h$_am_stamp_count +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_COMMANDS section. +# +for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue + ac_dest=`echo "$ac_file" | sed 's,:.*,,'` + ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_dir=`(dirname "$ac_dest") 2>/dev/null || +$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_dest" : 'X\(//\)[^/]' \| \ + X"$ac_dest" : 'X\(//\)$' \| \ + X"$ac_dest" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_dest" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + { echo "$as_me:$LINENO: executing $ac_dest commands" >&5 +echo "$as_me: executing $ac_dest commands" >&6;} + case $ac_dest in + depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`(dirname "$mf") 2>/dev/null || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`(dirname "$file") 2>/dev/null || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p $dirpart/$fdir + else + as_dir=$dirpart/$fdir + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5 +echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;} + { (exit 1); exit 1; }; }; } + + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done + ;; + esac +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + + +# Final message +echo "" +echo "Configuration is done." +echo "You can now build $PACKAGE_NAME by running:" +# test x$GXX = xyes && \ +# echo "% make depend [optional]" +echo "" +echo "% make" +echo "" +echo "Note: 'make install' has not been implemented yet. Avoid using." + + +# ---------------------------------------- +# CONFIG Template +# ---------------------------------------- + +# Fence added in configuration file + + + +# Stuff added at bottom of file + + + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000000..45d20522d3 --- /dev/null +++ b/configure.ac @@ -0,0 +1,338 @@ +# -*-Shell-script-*- +# +# Copyright (c) Luc Vincent + +# ---------------------------------------- +# Initialization +# ---------------------------------------- + +AC_PREREQ(2.50) +AC_INIT(Tesseract, 1.03, theraysmith@users.sourceforge.net) +AC_PACKAGE_TARNAME(tesseract-ocr) +AC_REVISION($Id: configure.ac,v 1.4 2007/02/02 22:38:17 theraysmith Exp $) +AC_CONFIG_AUX_DIR(config) +AC_CONFIG_SRCDIR(ccmain/tesseractmain.cpp) +AC_PREFIX_DEFAULT(/usr/local) +AC_CANONICAL_HOST + +# Define date of package, etc. Could be useful in auto-generated +# documentation. +# TODO(luc) Generate good documentation using doxygen or equivalent +PACKAGE_YEAR=2006 +PACKAGE_DATE="06/2006" + +AC_DEFINE_UNQUOTED(PACKAGE_NAME,["${PACKAGE_NAME}"],[Name of package]) +AC_DEFINE_UNQUOTED(PACKAGE_VERSION,["${PACKAGE_VERSION}"],[Version number]) +AC_DEFINE_UNQUOTED(PACKAGE_YEAR,"$PACKAGE_YEAR",[Official year for this release]) +AC_DEFINE_UNQUOTED(PACKAGE_DATE,"$PACKAGE_DATE",[Official date of release]) + +AC_SUBST(PACKAGE_NAME) +AC_SUBST(PACKAGE_VERSION) +AC_SUBST(PACKAGE_YEAR) +AC_SUBST(PACKAGE_DATE) + +AC_ARG_WITH(extra-includes, + AC_HELP_STRING([--with-extra-includes=DIR], + [Define an additional directory for include files]), + [ if test -d "$withval" ; then + CFLAGS="$CFLAGS -I$withval" + else + AC_MSG_ERROR([Cannot stat directory $withval]) + fi ] ) + +AC_ARG_WITH(extra-libraries, + AC_HELP_STRING([--with-extra-libraries=DIR], + [Define an additional directory for library files]), + [ if test -d "$withval" ; then + LDFLAGS="$LDFLAGS -L$withval" + else + AC_MSG_ERROR([Cannot stat directory $withval]) + fi ] ) + +# Always look into a "gnu" directory. +curwd=`pwd` +if test -d $curwd/gnu/include ; then + CPPFLAGS="$CPPFLAGS -I$curwd/gnu/include" +fi +if test -d $curwd/gnu/lib ; then + LDFLAGS="$LDFLAGS -L$curwd/gnu/lib" +fi + +# Special cases +case "$host" in + *-darwin* | *-macos10*) + if test -d /opt/local ; then + CPPFLAGS="$CPPFLAGS -I/opt/local/include" + LDFLAGS="$LDFLAGS -L/opt/local/lib" + elif test -d /sw ; then + CPPFLAGS="$CPPFLAGS -I/sw/include" + LDFLAGS="$LDFLAGS -L/sw/lib" + fi + ;; +esac + +# ---------------------------------------- +# Check Compiler Characteristics and +# configure automake. The two appear to +# be intimately linked... +# ---------------------------------------- + +# Define order of compilers +AC_PROG_CXX(cl.exe g++) +# Not needed +# AC_PROG_CC + +# Automake configuration +# ---------------------------------------- + +# Note: may need to configure automake to use ZIP as a distribution +# format because of an apparent bug with GZIP, which results in bogus +# archives. +# TODO(luc) Resolve this issue. +#AM_INIT_AUTOMAKE(dist-zip) +AM_INIT_AUTOMAKE +AM_CONFIG_HEADER(config_auto.h:config/config.h.in) +AM_PROG_CC_C_O +AM_MAINTAINER_MODE +# Need to tell automake if Visual C++ is being used: +AM_CONDITIONAL(USING_CL, test x$CC = xcl.exe) + +# Additional checking of compiler characteristics +# ---------------------------------------- + +# Check Endianness. If Big Endian, this will define WORDS_BIGENDIAN +# See also at end of this file, where we define INTEL_BYTE_ORDER +# or MOTOROLA_BYTE_ORDER. +AC_C_BIGENDIAN + + +# ---------------------------------------- +# Check for programs we need +# ---------------------------------------- + +# Check where all the following programs are and set +# variables accordingly: +AC_PROG_RANLIB +# AC_PROG_LN_S +# AC_PATH_PROG(MV, mv) +# AC_PATH_PROG(CP, cp) +# AC_PATH_PROG(RM, rm) +# AC_PATH_PROG(AR, ar) +# AC_PATH_PROG(TOUCH, touch) +# AC_PATH_PROG(SED, sed) +# AC_PATH_PROG(BASH, bash, ,[$PATH:/usr/bin:/util/tools/bin]) +# # To use substitution in makefiles, use something like: +# AC_SUBST(BASH) + +# TODO(luc) Handle documentation. None of the following +# is really needed until then +# +# AC_PROG_DOXYGEN_VERSION(1.3.2,[DOXYGEN_OK=1]) +# AC_PATH_PROG(DOT, dot) +# AC_PATH_PROG(LATEX, latex) +# AC_PATH_PROG(DVIPS, dvips) +# AC_PATH_PROG(MAKEINDEX, makeindex) +# AC_PATH_PROG(PDFLATEX, pdflatex) +# AC_PATH_PROG(GZIP, gzip) +# +# if test -z "$DOXYGEN_OK" -o -z "$DOT"; then +# AC_MSG_WARN([------------------------------------ +# *** Disabling automatic documentation generation for this +# *** package. Please check that you have 'doxygen' (version +# *** $ac_doxygen_version or later) and 'graphviz' (aka, 'dot') +# *** installed on your system. In addition, to generate +# *** PostScript and PDF documentation, you will need to have +# *** LaTeX and PdfLaTeX respectively. Re-run this configuration +# *** script after you have updated your environment. +# --------------------------------------------------------]) +# +# # We have appropriate version of doxygen and dot, so we +# # can generate documentation. It remains to be seen whether +# # we can generate PDF and PostScript documentation.. +# else +# GENERATE_DOCUMENTATION="true" +# +# # Determine if PostScript documentation is generated: +# if test -z "$LATEX" -o -z "$DVIPS" -o -z "$MAKEINDEX"; then +# AC_MSG_WARN([Disabling generation of PostScript documentation]) +# else +# GENERATE_PS_DOCUMENTATION="true" +# fi +# +# # Determine if PDF documentation is generated: +# if test -z "$PDFLATEX" -o -z "$MAKEINDEX"; then +# AC_MSG_WARN([Disabling generation of PDF documentation]) +# else +# GENERATE_PDF_DOCUMENTATION="true" +# fi +# fi +# +# # These substitutions could be inside the 'else' +# # conditionals above, but it is not necessary and would +# # only cause some confusion... +# AC_SUBST(DOXYGEN) +# AC_SUBST(DOT) +# AC_SUBST(LATEX) +# AC_SUBST(DVIPS) +# AC_SUBST(MAKEINDEX) +# AC_SUBST(PDFLATEX) +# AC_SUBST(GZIP) +# +# # Adjust makefiles based on the kind of documentation that +# # is being generated, +# AM_CONDITIONAL(GENERATE_DOCUMENTATION, test -n "$GENERATE_DOCUMENTATION") +# AM_CONDITIONAL(GENERATE_PS_DOCUMENTATION, test -n "$GENERATE_PS_DOCUMENTATION") +# AM_CONDITIONAL(GENERATE_PDF_DOCUMENTATION, test -n "$GENERATE_PDF_DOCUMENTATION") + + +# Test for GNUWIN32 tools (only useful under windows) +AC_PATH_GNUWIN32 + +# ---------------------------------------- +# C++ related options +# ---------------------------------------- + +AC_LANG_CPLUSPLUS + +# Enable --enable-debug or --disable-debug and set +# compile options accordingly. We are supposed to be either +# in debug mode or in optimize mode. Note that in debug mode, +# DEBUG_MODE will be set by this macro +AC_CXX_OPTIMIZE + +AC_CXX_BOOL +AC_CXX_TYPENAME +AC_CXX_STDINCLUDES +AC_CXX_RPO + +# ---------------------------------------- +# Check for libraries +# ---------------------------------------- + +# This option seems to always add -lm to the link line, +# which causes unnecessary warnings with Visual C++. +# Comment it out for now. +#AC_CHECK_LIB(m,sqrt) + + +# ---------------------------------------- +# Checks for header files. +# ---------------------------------------- + +AC_HEADER_STDC +AC_HEADER_TIME +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS(sys/ipc.h sys/shm.h) +AC_CHECK_HEADERS(limits.h malloc.h) +# Enable use of system-defined bool type if available: +AC_HEADER_STDBOOL + +# Misc +AC_SYS_INTERPRETER +AC_SYS_LARGEFILE + + +# ---------------------------------------- +# Checks for typedefs, structures, and compiler characteristics. +# ---------------------------------------- + +AC_CHECK_TYPES(wchar_t) +AC_CHECK_TYPES(long long int) +AC_CHECK_TYPES(mbstate_t,,,[#include "wchar.h"]) + +#AC_TYPE_MODE_T +#AC_TYPE_OFF_T +AC_TYPE_SIZE_T +#AC_TYPE_PID_T + + +# ---------------------------------------- +# Checks for library functions. +# ---------------------------------------- + +AC_FUNC_MMAP +AC_FUNC_FORK +AC_CHECK_FUNCS(strerror vsnprintf) +AC_CHECK_FUNCS(gethostname) +AC_CHECK_FUNCS(strchr memcpy) +AC_CHECK_FUNCS(acos asin) + +# ---------------------------------------- +# Test auxilliary packages +# ---------------------------------------- + +# Search JPEG library - not needed at the moment +# AC_PATH_JPEG(, +# [ no_jpeg=yes +# AC_MSG_WARN([JPEG support is disabled]) ]) + +# Search LIBTIFF library +AC_PATH_LIBTIFF(, +[ no_libtiff=yes + AC_MSG_WARN([TIFF support is disabled]) ]) + + +# ---------------------------------------- +# Final Tasks and Output +# ---------------------------------------- + +# Define installation paths +AC_DEFINE_INSTALL_PATHS +# Redundant with PACKAGE_VERSION - comment out +# AC_DEFINE_UNQUOTED(TESSERACT_VERSION,["${PACKAGE_VERSION}"],[version string]) + +# Output files +AC_CONFIG_FILES(Makefile) +AC_CONFIG_FILES(ccmain/Makefile) +AC_CONFIG_FILES(ccstruct/Makefile) +AC_CONFIG_FILES(ccutil/Makefile) +AC_CONFIG_FILES(classify/Makefile) +AC_CONFIG_FILES(cutil/Makefile) +AC_CONFIG_FILES(dict/Makefile) +AC_CONFIG_FILES(display/Makefile) +AC_CONFIG_FILES(image/Makefile) +AC_CONFIG_FILES(textord/Makefile) +AC_CONFIG_FILES(viewer/Makefile) +AC_CONFIG_FILES(wordrec/Makefile) +AC_CONFIG_FILES(training/Makefile) +# AC_CONFIG_FILES(doc/Doxyfile) +# AC_CONFIG_FILES(doc/header.html) +# AC_CONFIG_FILES(doc/footer.html) +# AC_CONFIG_FILES(doc/header.tex) +# AC_CONFIG_FILES(doc/RTF_ExtensionFile) +# AC_CONFIG_FILES(doc/Makefile) +AC_OUTPUT + +# Final message +echo "" +echo "Configuration is done." +echo "You can now build $PACKAGE_NAME by running:" +# test x$GXX = xyes && \ +# echo "% make depend [optional]" +echo "" +echo "% make" +echo "" +echo "Note: 'make install' has not been implemented yet. Avoid using." + + +# ---------------------------------------- +# CONFIG Template +# ---------------------------------------- + +# Fence added in configuration file +AH_TOP([ +#ifndef CONFIG_AUTO_H +#define CONFIG_AUTO_H +/* config_auto.h: begin */ +]) + +# Stuff added at bottom of file +AH_BOTTOM([ + +/* Miscellaneous defines */ +#define AUTOCONF 1 + +/* config_auto.h: end */ +#endif +]) + diff --git a/cutil/Makefile.am b/cutil/Makefile.am new file mode 100644 index 0000000000..f71b9858c4 --- /dev/null +++ b/cutil/Makefile.am @@ -0,0 +1,14 @@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + bitvec.h callcpp.h const.h cutil.h danerror.h debug.h efio.h \ + emalloc.h freelist.h funcdefs.h general.h globals.h listio.h \ + minmax.h oldheap.h oldlist.h structures.h tessarray.h \ + tordvars.h variables.h + +noinst_LIBRARIES = libtesseract_cutil.a +libtesseract_cutil_a_SOURCES = \ + tessarray.cpp bitvec.cpp cutil.cpp danerror.cpp debug.cpp efio.cpp \ + emalloc.cpp freelist.cpp globals.cpp listio.cpp oldheap.cpp \ + oldlist.cpp structures.cpp tordvars.cpp variables.cpp diff --git a/cutil/Makefile.in b/cutil/Makefile.in new file mode 100644 index 0000000000..3d54d88d3d --- /dev/null +++ b/cutil/Makefile.in @@ -0,0 +1,549 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = cutil +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_cutil_a_AR = $(AR) $(ARFLAGS) +libtesseract_cutil_a_LIBADD = +am_libtesseract_cutil_a_OBJECTS = tessarray.$(OBJEXT) bitvec.$(OBJEXT) \ + cutil.$(OBJEXT) danerror.$(OBJEXT) debug.$(OBJEXT) \ + efio.$(OBJEXT) emalloc.$(OBJEXT) freelist.$(OBJEXT) \ + globals.$(OBJEXT) listio.$(OBJEXT) oldheap.$(OBJEXT) \ + oldlist.$(OBJEXT) structures.$(OBJEXT) tordvars.$(OBJEXT) \ + variables.$(OBJEXT) +libtesseract_cutil_a_OBJECTS = $(am_libtesseract_cutil_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_cutil_a_SOURCES) +DIST_SOURCES = $(libtesseract_cutil_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/ccutil +EXTRA_DIST = \ + bitvec.h callcpp.h const.h cutil.h danerror.h debug.h efio.h \ + emalloc.h freelist.h funcdefs.h general.h globals.h listio.h \ + minmax.h oldheap.h oldlist.h structures.h tessarray.h \ + tordvars.h variables.h + +noinst_LIBRARIES = libtesseract_cutil.a +libtesseract_cutil_a_SOURCES = \ + tessarray.cpp bitvec.cpp cutil.cpp danerror.cpp debug.cpp efio.cpp \ + emalloc.cpp freelist.cpp globals.cpp listio.cpp oldheap.cpp \ + oldlist.cpp structures.cpp tordvars.cpp variables.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu cutil/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu cutil/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_cutil.a: $(libtesseract_cutil_a_OBJECTS) $(libtesseract_cutil_a_DEPENDENCIES) + -rm -f libtesseract_cutil.a + $(libtesseract_cutil_a_AR) libtesseract_cutil.a $(libtesseract_cutil_a_OBJECTS) $(libtesseract_cutil_a_LIBADD) + $(RANLIB) libtesseract_cutil.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitvec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cutil.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/danerror.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emalloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freelist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/globals.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/listio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldheap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldlist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/structures.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessarray.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tordvars.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/variables.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/cutil/bitvec.cpp b/cutil/bitvec.cpp new file mode 100644 index 0000000000..5952383bd6 --- /dev/null +++ b/cutil/bitvec.cpp @@ -0,0 +1,122 @@ +/****************************************************************************** + ** Filename: bitvec.c + ** Purpose: Routines for manipulating bit vectors + ** Author: Dan Johnson + ** History: Thu Mar 15 10:37:27 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "bitvec.h" +#include "emalloc.h" +#include "freelist.h" +#include "callcpp.h" + +#include + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +static int BitVectorCount = 0; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +BIT_VECTOR ExpandBitVector(BIT_VECTOR Vector, int NewNumBits) { +/* + ** Parameters: + ** Vector bit vector to be expanded + ** NewNumBits new size of bit vector + ** Globals: none + ** Operation: This routine uses realloc to increase the size of + ** the specified bit vector. + ** Return: New expanded bit vector. + ** Exceptions: none + ** History: Fri Nov 16 10:11:16 1990, DSJ, Created. + */ + return ((BIT_VECTOR) Erealloc (Vector, + sizeof (unsigned long) * + WordsInVectorOfSize (NewNumBits))); + +} /* ExpandBitVector */ + + +/*---------------------------------------------------------------------------*/ +void FreeBitVector(BIT_VECTOR BitVector) { +/* + ** Parameters: + ** BitVector bit vector to be freed + ** Globals: + ** BitVectorCount count of number of bit vectors allocated + ** Operation: This routine frees a bit vector. It also decrements + ** the global counter that keeps track of the number of + ** bit vectors allocated. If BitVector is NULL, then + ** the count is printed to stderr. + ** Return: none + ** Exceptions: none + ** History: Tue Oct 23 16:46:09 1990, DSJ, Created. + */ + if (BitVector) { + memfree(BitVector); + BitVectorCount--; + } + else { + cprintf ("%6d BITVECTOR elements in use\n", BitVectorCount); + } + +} /* FreeBitVector */ + + + /*hamming_distance(array1,array2,length) computes the hamming distance + between two bit strings */ +/*--------------------------------------------------------------------------*/ +int hamming_distance( /*arrays to match */ + register unsigned long *array1, + register unsigned long *array2, + register int length) { /*length of arrays */ + register unsigned long diff; /*bit difference */ + register int dist; /*total distance */ + + dist = 0; + for (; length > 0; length--) { + diff = *array1++ ^ *array2++;/*different bits */ + while (diff) { + diff &= diff - 1; /*lose a bit */ + dist++; + } + } + return dist; /*total distance */ +} + + +/*---------------------------------------------------------------------------*/ +BIT_VECTOR NewBitVector(int NumBits) { +/* + ** Parameters: + ** NumBits number of bits in new bit vector + ** Globals: + ** BitVectorCount number of bit vectors allocated + ** Operation: Allocate and return a new bit vector large enough to + ** hold the specified number of bits. + ** Return: New bit vector. + ** Exceptions: none + ** History: Tue Oct 23 16:51:27 1990, DSJ, Created. + */ + BitVectorCount++; + return ((BIT_VECTOR) Emalloc (sizeof (unsigned long) * + WordsInVectorOfSize (NumBits))); + +} /* NewBitVector */ diff --git a/cutil/bitvec.h b/cutil/bitvec.h new file mode 100644 index 0000000000..bfcc3a554a --- /dev/null +++ b/cutil/bitvec.h @@ -0,0 +1,100 @@ +/****************************************************************************** + ** Filename: bitvec.h + ** Purpose: Routines for manipulating bit vectors + ** Author: Dan Johnson + ** History: Wed Mar 7 17:52:45 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef BITVEC_H +#define BITVEC_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#define BITSINLONG 32 /*no of bits in a long */ +typedef unsigned int *BIT_VECTOR; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +#define zero_all_bits(array,length) \ +{\ + register int index; /*temporary index*/\ +\ +for (index=0;index or + * + * Revision 1.2 90/01/15 13:02:13 13:02:13 marks (Mark Seaman) + * Added memory allocator (*allocate) and (*deallocate) + * + * Revision 1.1 89/10/09 14:58:29 14:58:29 marks (Mark Seaman) + * Initial revision + **/ + +#include "cutil.h" +#include "callcpp.h" + +#include + +#define RESET_COUNT 2000 + +void_proc deallocate = (void_proc) c_free_string; +char_proc allocate = (char_proc) c_alloc_string; + +/********************************************************************** + * long_rand + * + * Return a long random number whose value is less than limit. Do this + * by calling the standard cheepo random number generator and reseting + * it pretty often. + **********************************************************************/ +long long_rand(long limit) { +#if RAND_MAX < 0x1000000 + static long seed; + + long num; + num = (long) rand () << 16; + num |= rand () & 0xffff; + seed ^= num; + long result = num % limit; + while (result < 0) { + result += limit; + } + return result; +#else + return (long)((double)limit * rand()/(RAND_MAX + 1.0)); +#endif +} + + +/********************************************************************** + * open_file + * + * Open a file for reading or writing. If the file name parameter is + * NULL use stdin (or stdout) for the file. If the file can not be + * opened then call the error routine. + **********************************************************************/ +FILE *open_file(const char *filename, const char *mode) { + FILE *thisfile = NULL; + if ((thisfile = fopen (filename, mode)) == NULL) { + printf ("Could not open file, %s\n", filename); + exit (1); + } + return (thisfile); +} diff --git a/cutil/cutil.h b/cutil/cutil.h new file mode 100644 index 0000000000..7b183cbb04 --- /dev/null +++ b/cutil/cutil.h @@ -0,0 +1,159 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: cutil.h + * Description: General utility functions + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Wed Dec 5 15:40:26 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** +$Log: cutil.h,v $ +Revision 1.1 2007/02/02 23:39:07 theraysmith +Fixed portability issues + +Revision 1.1.1.1 2004/02/20 19:39:06 slumos +Import original HP distribution + +*/ + +#ifndef CUTILH +#define CUTILH + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include +#include +#include + +#include "general.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define CHARS_PER_LINE 500 + +#if defined(__STDC__) || defined(__cplusplus) || MAC_OR_DOS +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif + +//typedef int (*int_proc) (void); +typedef void (*void_proc) (...); +typedef char *(*char_proc) _ARGS ((...)); +typedef void *(*void_star_proc) _ARGS ((...)); + +typedef int (*int_void) (void); +typedef void (*void_void) (void); +typedef int (*int_compare) (void *, void *); +typedef void (*void_dest) (void *); + +extern void_proc deallocate; +extern char_proc allocate; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * min + * + * Minimum of two values + **********************************************************************/ + +#ifndef min +#define min(x,y) \ + ((x) < (y) ? (x) : (y)) +#endif + +/********************************************************************** + * max + * + * Maximum of two values + **********************************************************************/ + +#ifndef max +#define max(x,y) \ + ((y) < (x) ? (x) : (y)) +#endif + +/********************************************************************** + * new_line + * + * Print a new line character on stdout. + **********************************************************************/ + +#define new_line() \ + printf ("\n") + +/********************************************************************** + * print_string + * + * Print a string on stdout. + **********************************************************************/ + +#define print_string(str) \ + printf ("%s\n", str) + +/********************************************************************** + * strfree + * + * Reserve a spot in memory for the string to be stored. Copy the string + * to it and return the result. + **********************************************************************/ + +#define strfree(s) ((*deallocate) (s)) + +/********************************************************************** + * strsave + * + * Reserve a spot in memory for the string to be stored. Copy the string + * to it and return the result. + **********************************************************************/ + +#define strsave(s) \ + ((s) ? \ + ((char*) strcpy ((*allocate) (strlen(s)+1), s)) : \ + (NULL)) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +long long_rand(long limit); + +FILE *open_file(const char *filename, const char *mode); + +/* util.c +long long_rand + _ARGS ((long limit)); + +FILE *open_file + _ARGS((char *filename, + char *mode)); + +#undef _ARGS +*/ +#endif diff --git a/cutil/danerror.cpp b/cutil/danerror.cpp new file mode 100644 index 0000000000..874ea169d7 --- /dev/null +++ b/cutil/danerror.cpp @@ -0,0 +1,145 @@ +/****************************************************************************** + ** Filename: danerror.c + ** Purpose: Routines for managing error trapping + ** Author: Dan Johnson + ** History: 3/17/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "general.h" +#include "danerror.h" +#include "callcpp.h" +#include "globaloc.h" +#ifdef __UNIX__ +#include "assert.h" +#endif + +#include +#include + +#define MAXTRAPDEPTH 100 + +#define ERRORTRAPDEPTH 1000 + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +static jmp_buf ErrorTrapStack[MAXTRAPDEPTH]; +static VOID_PROC ProcTrapStack[MAXTRAPDEPTH]; +static INT32 CurrentTrapDepth = 0; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ReleaseErrorTrap() { +/* + ** Parameters: + ** None + ** Globals: + ** CurrentTrapDepth number of traps on the stack + ** Operation: + ** This routine removes the current error trap from the + ** error trap stack, thus returning control to the previous + ** error trap. If the error trap stack is empty, nothing is + ** done. + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 4/3/89, DSJ, Created. + */ + if (CurrentTrapDepth > 0) { + CurrentTrapDepth--; + } +} /* ReleaseErrorTrap */ + + +/*---------------------------------------------------------------------------*/ +void DoError(int Error, const char *Message) { +/* + ** Parameters: + ** Error error number which is to be trapped + ** Message pointer to a string to be printed as an error message + ** Globals: + ** ErrorTrapStack stack of error traps + ** CurrentTrapDepth number of traps on the stack + ** Operation: + ** This routine prints the specified error message to stderr. + ** It then jumps to the current error trap. If the error trap + ** stack is empty, the calling program is terminated with a + ** fatal error message. + ** Return: + ** None - this routine does not return. + ** Exceptions: + ** Empty error trap stack terminates the calling program. + ** History: + ** 4/3/89, DSJ, Created. + */ + if (Message != NULL) { + cprintf ("\nError: %s!\n", Message); + } + + if (CurrentTrapDepth <= 0) { + cprintf ("\nFatal error: No error trap defined!\n"); + + /* SPC 20/4/94 + There used to be a call to abort() here. I've changed it to call into the + C++ error code to generate a meaningful status code + */ + signal_termination_handler(Error); + } + + if (ProcTrapStack[CurrentTrapDepth - 1] != DO_NOTHING) + (*ProcTrapStack[CurrentTrapDepth - 1]) (); + + longjmp (ErrorTrapStack[CurrentTrapDepth - 1], 1); + assert(FALSE); +} /* DoError */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +jmp_buf &PushErrorTrap(VOID_PROC Procedure) { +/* + ** Parameters: + ** Procedure trap procedure to execute + ** Globals: + ** ErrorTrapStack stack of error traps + ** CurrentTrapDepth number of traps on the stack + ** Operation: + ** This routine pushes a new error trap onto the top of + ** the error trap stack. This new error trap can then be + ** used in a call to setjmp. This trap is then in effect + ** until ReleaseErrorTrap is called. WARNING: a procedure + ** that calls PushErrorTrap should never exit before calling + ** ReleaseErrorTrap. + ** Return: + ** Pointer to a new error trap buffer + ** Exceptions: + ** Traps an error if the error trap stack is already full + ** History: + ** 3/17/89, DSJ, Created. + ** 9/12/90, DSJ, Added trap procedure parameter. + */ + if (CurrentTrapDepth >= MAXTRAPDEPTH) + DoError (ERRORTRAPDEPTH, "Error trap depth exceeded"); + ProcTrapStack[CurrentTrapDepth] = Procedure; + return ErrorTrapStack[CurrentTrapDepth++]; + +} /* PushErrorTrap */ diff --git a/cutil/danerror.h b/cutil/danerror.h new file mode 100644 index 0000000000..23cd014d65 --- /dev/null +++ b/cutil/danerror.h @@ -0,0 +1,41 @@ +/****************************************************************************** + ** Filename: danerror.h + ** Purpose: Definition of error trapping routines. + ** Author: Dan Johnson + ** History: 4/3/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef DANERROR_H +#define DANERROR_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include + +#define SetErrorTrap(Proc) setjmp(PushErrorTrap(Proc)) +#define NOERROR 0 +#define DO_NOTHING 0 + +typedef int TRAPERROR; +typedef void (*VOID_PROC) (); + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void ReleaseErrorTrap(); + +void DoError(int Error, const char *Message); + +jmp_buf &PushErrorTrap(VOID_PROC Procedure); +#endif diff --git a/cutil/debug.cpp b/cutil/debug.cpp new file mode 100644 index 0000000000..70fde48f1d --- /dev/null +++ b/cutil/debug.cpp @@ -0,0 +1,97 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: debug.c (Formerly debug.c) + * Description: Combinatorial Splitter + * Author: Mark Seaman, OCR Technology + * Created: Thu Jul 27 08:59:01 1989 + * Modified: Tue Feb 19 10:34:36 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include +#include "debug.h" +#include "callcpp.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +MENU_ITEM menu_table[NUM_MENUS][NUM_MENU_ITEMS]; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * set_float_value + * + * Set the value of a floating point variable from the break handler. + **********************************************************************/ +int set_float_value(const char *message, float *variable) { + char this_string[CHARS_PER_LINE]; + + cprintf ("%s (%7.5f) ? ", message, *variable); + fflush(stdout); + + if (fgets (this_string, CHARS_PER_LINE, stdin) == NULL) + return (1); + + if (fgets (this_string, CHARS_PER_LINE, stdin) != NULL) { + sscanf (this_string, "%f", variable); + cprintf ("%s = %7.5f\n", message, *variable); + } + return (1); +} + + +/********************************************************************** + * set_int_value + * + * Set the value of a floating point variable from the break handler. + **********************************************************************/ +int set_int_value(const char *message, int *variable) { + char this_string[CHARS_PER_LINE]; + + cprintf ("%s (%d) ? ", message, *variable); + fflush(stdout); + + if (fgets (this_string, CHARS_PER_LINE, stdin) == NULL) + return (1); + + if (fgets (this_string, CHARS_PER_LINE, stdin) != NULL) { + sscanf (this_string, "%d", variable); + cprintf ("%s = %d\n", message, *variable); + } + return (1); +} + + +/********************************************************************** + * make_menu_item + * + * Create an entry in the menu handler table that will create a menu + * entry. When this entry is selected it will invoke the requested + * function. + **********************************************************************/ +void make_menu_item(int menu, + int menu_item, + const char *menu_string, + int_void menu_funct) { + menu_table[menu][menu_item].menu_string = menu_string; + menu_table[menu][menu_item].menu_function = (void_proc) menu_funct; +} diff --git a/cutil/debug.h b/cutil/debug.h new file mode 100644 index 0000000000..7905c9fa84 --- /dev/null +++ b/cutil/debug.h @@ -0,0 +1,348 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: debug.h (Formerly debug.h) + * Description: Combinatorial Splitter + * Author: Mark Seaman, OCR Technology + * Created: Thu Jul 27 08:59:01 1989 + * Modified: Wed Feb 27 14:38:16 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/* To hide variable names for relase to UNLV DONT put the variable names in + the executable: Toggle be either defining or not defining SECURE_NAMES + This stage prevents the menu construction*/ +/* #define SECURE_NAMES done in secnames.h when necessary */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include "variables.h" +#include "callcpp.h" +#include + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef struct +{ + const char *menu_string; + void_proc menu_function; +} MENU_ITEM; + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +#define NUM_MENUS 30 +#define NUM_MENU_ITEMS 30 +extern MENU_ITEM menu_table[NUM_MENUS][NUM_MENU_ITEMS]; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * float_value + * + * Template procedures to set a floating point value of a variable. + **********************************************************************/ + +#define float_value(proc,string,variable,default) \ + \ +float variable = default; \ + \ +int proc () \ +{ \ + return (set_float_value (string, &variable)); \ +} \ + + +/********************************************************************** + * handle_menu_x + * + * Create a procedure to handle menu items. + **********************************************************************/ + +#define handle_menu(menu,handle_menu_x) \ + \ +int handle_menu_x () \ +{ \ + int x; \ + cprintf ("\t 0. Continue\n"); \ + for (x = 0; x < NUM_MENU_ITEMS; x++) { \ + if (menu_table[menu][x].menu_string) \ + cprintf ("\t%2d. %s\n", x, menu_table[menu][x].menu_string); \ + } \ + \ + scanf ("%d", &x); \ + \ + if (x == 0) return (0); \ + if ((0 < x && x < NUM_MENU_ITEMS) && \ + (menu_table[menu][x].menu_function)) { \ + (*menu_table[menu][x].menu_function) (); \ + return (1); \ + } \ + else { \ + cprintf ("Bad menu selection"); \ + return (0); \ + } \ +} \ + + +/********************************************************************** + * int_value + * + * Template procedures to set a floating point value of a variable. + **********************************************************************/ + +#define int_value(proc,string,variable,default) \ + \ +int variable = default; \ + \ +int proc () \ +{ \ + return (set_int_value (string, &variable)); \ +} \ + + +/********************************************************************** + * toggle_value + * + * Template procedures to toggle the value of a variable. + **********************************************************************/ + +#ifdef SECURE_NAMES +#define toggle_value(proc,string,variable,default) \ + \ +int variable = default; \ + \ +int proc () \ +{ \ + if (variable) { \ + variable = 0; \ + } \ + else { \ + variable = 1; \ + } \ + return (1); \ +} \ + +#else + +#define toggle_value(proc,string,variable,default) \ + \ +int variable = default; \ + \ +int proc () \ +{ \ + if (variable) { \ + cprintf( "%s is OFF\n", string); \ + variable = 0; \ + } \ + else { \ + cprintf( "%s is ON\n", string); \ + variable = 1; \ + } \ + return (1); \ +} \ + +#endif + +/********************************************************************** + * make_float_const + * + * Create a constant with a config file reader + **********************************************************************/ + +#define make_float_const(name,default,installer) \ + \ +float name = default; \ + \ +void installer () \ +{ \ +float_variable (name, #name, default); \ +} \ + + +/********************************************************************** + * make_int_const + * + * Create a constant with a config file reader + **********************************************************************/ + +#define make_int_const(name,default,installer) \ + \ +int name = default; \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +} \ + + +/********************************************************************** + * make_toggle_const + * + * Create a constant with a config file reader + **********************************************************************/ + +#define make_toggle_const(name,default,installer) \ + \ +int name = default; \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +} \ + + +#ifdef SECURE_NAMES +/********************************************************************** + * make_float_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_float_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +float_value (menufunct, "", name, default); \ + \ +void installer () \ +{ \ +float_variable (name, #name, default); \ +} \ + + +/********************************************************************** + * make_int_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_int_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +int_value (menufunct, "", name, default); \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +} \ + + +/********************************************************************** + * make_toggle_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_toggle_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +toggle_value (menufunct, "", name, default); \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +} \ + +#else + +/********************************************************************** + * make_float_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_float_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +float_value (menufunct, menustring, name, default); \ + \ +void installer () \ +{ \ +float_variable (name, #name, default); \ +make_menu_item (menu, menuitem, menustring, menufunct); \ +} \ + + +/********************************************************************** + * make_int_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_int_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +int_value (menufunct, menustring, name, default); \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +make_menu_item (menu, menuitem, menustring, menufunct); \ +} \ + + +/********************************************************************** + * make_toggle_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_toggle_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +toggle_value (menufunct, menustring, name, default); \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +make_menu_item (menu, menuitem, menustring, menufunct); \ +} \ + +#endif + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +int set_float_value(const char *message, float *variable); + +int set_int_value(const char *message, int *variable); + +void make_menu_item(int menu, + int menu_item, + const char *menu_string, + int_void menu_funct); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif + +int set_float_value +_ARGS((char *message, + float *variable)); + +int set_int_value +_ARGS((char *message, + int *variable)); + +void make_menu_item +_ARGS((int menu, + int menu_item, + char *menu_string, + int_proc menu_funct)); + +#undef _ARGS +*/ +#endif diff --git a/cutil/efio.cpp b/cutil/efio.cpp new file mode 100644 index 0000000000..20daae927d --- /dev/null +++ b/cutil/efio.cpp @@ -0,0 +1,62 @@ +/****************************************************************************** + ** Filename: efio.c + ** Purpose: Utility I/O routines + ** Author: Dan Johnson + ** History: 5/21/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "efio.h" +#include "danerror.h" +#include +#include + +#define MAXERRORMESSAGE 256 + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FILE *Efopen(const char *Name, const char *Mode) { +/* + ** Parameters: + ** Name name of file to be opened + ** Mode mode to be used to open file + ** Globals: + ** None + ** Operation: + ** This routine attempts to open the specified file in the + ** specified mode. If the file can be opened, a pointer to + ** the open file is returned. If the file cannot be opened, + ** an error is trapped. + ** Return: + ** Pointer to open file. + ** Exceptions: + ** FOPENERROR unable to open specified file + ** History: + ** 5/21/89, DSJ, Created. + */ + FILE *File; + char ErrorMessage[MAXERRORMESSAGE]; + + File = fopen (Name, Mode); + if (File == NULL) { + sprintf (ErrorMessage, "Unable to open %s", Name); + DoError(FOPENERROR, ErrorMessage); + return (NULL); + } + else + return (File); +} /* Efopen */ diff --git a/cutil/efio.h b/cutil/efio.h new file mode 100644 index 0000000000..ad706efa4d --- /dev/null +++ b/cutil/efio.h @@ -0,0 +1,32 @@ +/****************************************************************************** + ** Filename: efio.h + ** Purpose: Definition of file I/O routines + ** Author: Dan Johnson + ** History: 5/21/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef EFIO_H +#define EFIO_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include + +#define FOPENERROR 3000 + +/**---------------------------------------------------------------------------- + Public Function Prototype +----------------------------------------------------------------------------**/ +FILE *Efopen(const char *Name, const char *Mode); +#endif diff --git a/cutil/emalloc.cpp b/cutil/emalloc.cpp new file mode 100644 index 0000000000..a9679c95b0 --- /dev/null +++ b/cutil/emalloc.cpp @@ -0,0 +1,91 @@ +/****************************************************************************** + ** Filename: + emalloc.c +** Purpose: + Routines for trapping memory allocation errors. +** Author: + Dan Johnson + HP-UX 6.2 + HP-UX 6.2 +** History: + 4/3/89, DSJ, Created. +** +** (c) Copyright Hewlett-Packard Company, 1988. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "emalloc.h" +#include "danerror.h" +#include + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void *Emalloc(size_t Size) { +/* + ** Parameters: + ** Size + number of bytes of memory to be allocated +** Globals: none +** Operation: +** This routine attempts to allocate the specified number of +** bytes. If the memory can be allocated, a pointer to the +** memory is returned. If the memory cannot be allocated, or +** if the allocation request is negative or zero, +** an error is trapped. +** Return: Pointer to allocated memory. +** Exceptions: NOTENOUGHMEMORY + unable to allocate Size bytes +** ILLEGALMALLOCREQUEST + negative or zero request size +** History: 4/3/89, DSJ, Created. +*/ + void *Buffer; + + if (Size <= 0) + DoError (ILLEGALMALLOCREQUEST, "Illegal malloc request size"); + Buffer = (void *) malloc (Size); + if (Buffer == NULL) { + DoError (NOTENOUGHMEMORY, "Not enough memory"); + return (NULL); + } + else + return (Buffer); + +} /* Emalloc */ + + +/*---------------------------------------------------------------------------*/ +void *Erealloc(void *ptr, size_t size) { + void *Buffer; + + if (size < 0 || (size == 0 && ptr == NULL)) + DoError (ILLEGALMALLOCREQUEST, "Illegal realloc request size"); + + Buffer = (void *) realloc (ptr, size); + if (Buffer == NULL && size != 0) + DoError (NOTENOUGHMEMORY, "Not enough memory"); + return (Buffer); + +} /* Erealloc */ + + +/*---------------------------------------------------------------------------*/ +void Efree(void *ptr) { + if (ptr == NULL) + DoError (ILLEGALMALLOCREQUEST, "Attempted to free NULL ptr"); + + free(ptr); + +} /* Efree */ diff --git a/cutil/emalloc.h b/cutil/emalloc.h new file mode 100644 index 0000000000..c6dbb37463 --- /dev/null +++ b/cutil/emalloc.h @@ -0,0 +1,72 @@ +/****************************************************************************** + ** Filename: emalloc.h + ** Purpose: Definition of memory allocation routines. + ** Author: Dan Johnson + ** History: 4/3/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef EMALLOC_H +#define EMALLOC_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "host.h" +#include "callcpp.h" + +#define NOTENOUGHMEMORY 2000 +#define ILLEGALMALLOCREQUEST 2001 + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void *Emalloc(size_t Size); + +void *Erealloc(void *ptr, size_t size); + +void Efree(void *ptr); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* emalloc.c +void *Emalloc + _ARGS((size_t Size)); + +void *Erealloc + _ARGS((void *ptr, + size_t size)); + +void Efree + _ARGS((void *ptr)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ + +//extern void* c_alloc_struct(); +//#define alloc_struct c_alloc_struct +/*extern void c_free_struct(void* + deadstruct, //structure to free +INT32 count, //no of bytes +const char* name //class name +);*/ +//#define free_struct c_free_struct +#endif diff --git a/cutil/freelist.cpp b/cutil/freelist.cpp new file mode 100644 index 0000000000..72b46de267 --- /dev/null +++ b/cutil/freelist.cpp @@ -0,0 +1,85 @@ +/************************************************************************** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. +**************************************************************************/ +#include "freelist.h" +#include "danerror.h" +#include "callcpp.h" + +#include + +static int mem_alloc_counter = 0; + +/********************************************************************** + * memalloc_p + * + * Memory allocator with protection. + **********************************************************************/ +int *memalloc_p(int size) { + mem_alloc_counter++; + if (!size) + DoError (0, "Allocation of 0 bytes"); + return ((int *) c_alloc_mem_p (size)); +} + + +/********************************************************************** + * memalloc + * + * Memory allocator with protection. + **********************************************************************/ +int *memalloc(int size) { + mem_alloc_counter++; + return ((int *) c_alloc_mem (size)); +} + + +/********************************************************************** + * memrealloc + * + * Memory allocator with protection. + **********************************************************************/ +int *memrealloc(void *ptr, int size, int oldsize) { + int shiftsize; + int *newbuf; + + shiftsize = size > oldsize ? oldsize : size; + newbuf = (int *) c_alloc_mem (size); + memcpy(newbuf, ptr, shiftsize); + c_free_mem(ptr); + return newbuf; +} + + +/********************************************************************** + * memfree + * + * Memory allocator with protection. + **********************************************************************/ +void memfree(void *element) { + if (element) { + c_free_mem(element); + mem_alloc_counter--; + } + else { + cprintf ("%d MEM_ALLOC's used\n", mem_alloc_counter); + DoError (0, "Memfree of NULL pointer"); + } +} + + +/********************************************************************** + * mem_tidy + * + * Do nothing. + **********************************************************************/ +void mem_tidy(int level) { + c_check_mem ("Old tidy", level); +} diff --git a/cutil/freelist.h b/cutil/freelist.h new file mode 100644 index 0000000000..159c10d45e --- /dev/null +++ b/cutil/freelist.h @@ -0,0 +1,69 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: freelist.h (Formerly freelist.h) + * Description: Memory allocator + * Author: Mark Seaman, OCR Technology + * Created: Wed May 30 13:50:28 1990 + * Modified: Mon Dec 10 15:15:25 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ + +#ifndef FREELIST_H +#define FREELIST_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +int *memalloc_p(int size); + +int *memalloc(int size); + +int *memrealloc(void *ptr, int size, int oldsize); + +void memfree(void *element); + +void mem_tidy(int level); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* freelist.c +int *memalloc_p + _ARGS((int size)); + +int *memalloc + _ARGS((int size)); + +void memfree + _ARGS((void *element)); + +void mem_tidy + _ARGS((void)); + +#undef _ARGS +*/ +#endif diff --git a/cutil/funcdefs.h b/cutil/funcdefs.h new file mode 100644 index 0000000000..7426f5b115 --- /dev/null +++ b/cutil/funcdefs.h @@ -0,0 +1,55 @@ +/****************************************************************************** + ** Filename: funcdefs.h + ** Purpose: Definition of function types for passing as params. + ** Author: Dan Johnson + ** History: Fri Sep 14 10:04:47 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef FUNCDEFS_H +#define FUNCDEFS_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "general.h" + +typedef INT8 (*INT8_FUNC) (); +typedef UINT8 (*UINT8_FUNC) (); +typedef INT16 (*INT16_FUNC) (); +typedef UINT16 (*UINT16_FUNC) (); +typedef INT32 (*INT32_FUNC) (); +typedef UINT32 (*UINT32_FUNC) (); +typedef FLOAT32 (*FLOAT32_FUNC) (); +typedef FLOAT64 (*FLOAT64_FUNC) (); +typedef PINT8 (*PINT8_FUNC) (); +typedef PUINT8 (*PUINT8_FUNC) (); +typedef PINT16 (*PINT16_FUNC) (); +typedef PUINT16 (*PUINT16_FUNC) (); +typedef PINT32 (*PINT32_FUNC) (); +typedef PUINT32 (*PUINT32_FUNC) (); +typedef PFLOAT32 (*PFLOAT32_FUNC) (); +typedef PFLOAT64 (*PFLOAT64_FUNC) (); + +typedef CHAR (*CHAR_FUNC) (); +typedef BOOL8 (*BOOL8_FUNC) (); +typedef int (*INT_FUNC) (); +typedef void (*VOID_FUNC) (); + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#endif diff --git a/cutil/general.h b/cutil/general.h new file mode 100644 index 0000000000..2c51d36a45 --- /dev/null +++ b/cutil/general.h @@ -0,0 +1,33 @@ +/****************************************************************************** + ** Filename: General.h + ** Purpose: this is the system independent typedefs and defines + ** Author: Mike Niquette / Dan Johnson + ** History: Creation Date: 09/13/1988, MLN + ** Added UNIX: 11/10/88, DSJ + ** Changed name to General.h 11/24/88, DSJ + ** Added BOOL, CHAR, TRUE, FALSE, 11/24/88, DSJ + ** STATUS + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef GENERAL_H +#define GENERAL_H + +#include "host.h" + +typedef char CHAR; +typedef int STATUS; + +#ifndef NULL +#define NULL 0 +#endif +#endif diff --git a/cutil/globals.cpp b/cutil/globals.cpp new file mode 100644 index 0000000000..22f8d96a49 --- /dev/null +++ b/cutil/globals.cpp @@ -0,0 +1,65 @@ +/* +################################################################################ +# +# File: globals.c +# Description: Global flag definitions +# Author: Mark Seaman, OCR Technology +# Created: Thu Oct 19 16:51:26 1989 +# Modified: Fri Jan 26 13:16:37 1990 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1989, Hewlett-Packard Company. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +# +################################################################################ +**************************************************************************/ +#include +#include "globals.h" + +/* This file contains the global declarations used by all demonstrator files*/ + +//IMAGE info; /*image info record*/ +TBLOB *pageblobs; /*first blob on page */ +TEXTBLOCK *pageblocks; /*first block on page */ +char classes[CLASSIZE][CLASSLENGTH]; + /*class definitions */ + /*indices to to_classes */ + +int resolution; /*scanner res in dpi */ +int acts[MAXPROC]; /*action flags */ +int debugs[MAXPROC]; /*debug flags */ +int plots[MAXPROC]; /*plot flags */ + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +int corners[4]; /*corners of scan window */ + +char imagefile[FILENAMESIZE]; /*image file name */ +char directory[FILENAMESIZE]; /* main directory */ +char *debugfile; /* debug file name */ + +int plots_fx; +int plots_ocr; + +int debugs_fx; +int debugs_ocr; + +int acts_fx; +int acts_ocr; + +char *demodir; /*demo home directory */ + +int edgefd; /*edges window */ +int debugfd; /*debug window fd */ +FILE *debugfp; /*debug log file */ diff --git a/cutil/globals.h b/cutil/globals.h new file mode 100644 index 0000000000..7f86be89af --- /dev/null +++ b/cutil/globals.h @@ -0,0 +1,65 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: globals.h (Formerly globals.h) + * Description: Global Variables for Wise Owl + * Author: Mark Seaman, OCR Technology + * Created: Thu Dec 21 11:38:36 1989 + * Modified: Thu Jan 4 17:13:00 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef GLOBALS_H +#define GLOBALS_H + +#include "tessclas.h" +#include "const.h" + +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +//extern IMAGE info; /*image info record*/ +extern TBLOB *pageblobs; /*first blob on page */ +extern TEXTBLOCK *pageblocks; /*first block on page */ + /*class definitions */ +extern char classes[CLASSIZE][CLASSLENGTH]; +extern int resolution; /*scanner res in dpi */ +extern int acts[MAXPROC]; /*action flags */ +extern int debugs[MAXPROC]; /*debug flags */ +extern int plots[MAXPROC]; /*plot flags */ +extern int corners[4]; /*corners of scan window */ +extern int optind; /*option index */ +extern char *optarg; /*option argument */ + /*image file name */ +extern char imagefile[FILENAMESIZE]; + /* main directory */ +extern char directory[FILENAMESIZE]; +extern char *debugfile; /* debug file name */ + +extern int plots_fx; +extern int plots_ocr; + +extern int debugs_fx; +extern int debugs_ocr; + +extern int acts_fx; +extern int acts_ocr; + +extern char *demodir; +extern FILE *debugfp; /*debug log file */ +#endif diff --git a/cutil/listio.cpp b/cutil/listio.cpp new file mode 100644 index 0000000000..5c9894dffe --- /dev/null +++ b/cutil/listio.cpp @@ -0,0 +1,68 @@ +/* -*-C-*- +################################################################################ +# +# File: listio.c +# Description: List I/O processing procedures. +# Author: Mark Seaman, Software Productivity +# Created: Thu Jul 23 13:24:09 1987 +# Modified: Fri May 17 17:33:30 1991 (Mark Seaman) marks@hpgrlt +# Language: C +# Package: N/A +# Status: Reusable Software Component +# +# (c) Copyright 1987, Hewlett-Packard Company. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +# +################################################################################ + +This file contains the implementations of a set of general purpose +list I/O routines. For the interface definitions look in the file +"listio.h". +---------------------------------------------------------------------------*/ + +#include +#include +#include +#include "listio.h" + +/*--------------------------------------------------------------------------- + Public Function Code +---------------------------------------------------------------------------*/ +/************************************************************************* + * R E A D L I S T + * + * Read a list of strings from a file. Return the string list to the + * caller. + *************************************************************************/ +LIST read_list(const char *filename) { + FILE *infile; + char s[CHARS_PER_LINE]; + LIST list; + char *chopAt250(); + + if ((infile = open_file (filename, "r")) == NULL) + return (NIL); + + list = NIL; + while (fgets (s, CHARS_PER_LINE, infile) != NULL) { + s[CHARS_PER_LINE - 1] = '\0'; + if (strlen (s) > 0) { + if (s[strlen (s) - 1] == '\n') + s[strlen (s) - 1] = '\0'; + if (strlen (s) > 0) { + list = push (list, (LIST) strsave (s)); + } + } + } + + fclose(infile); + return (reverse_d (list)); +} diff --git a/cutil/listio.h b/cutil/listio.h new file mode 100644 index 0000000000..e758c9bcb2 --- /dev/null +++ b/cutil/listio.h @@ -0,0 +1,43 @@ +/* -*-C-*- +################################################################################ +# +# File: listio.h +# Description: List I/O processing procedures. +# Author: Mark Seaman, Software Productivity +# Created: Thu Jul 23 13:24:09 1987 +# Modified: Mon Oct 16 11:38:52 1989 (Mark Seaman) marks@hpgrlt +# Language: C +# Package: N/A +# Status: Reusable Software Component +# +# (c) Copyright 1987, Hewlett-Packard Company. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +# +################################################################################ + * Revision 1.5 89/06/27 11:56:00 11:56:00 marks (Mark Seaman) + * Fixed MAC_OR_DOS bug + * + + This file contains the interface definitions to a set of general purpose + list I/O routines. + +***********************************************************************/ +#ifndef LISTIO_H +#define LISTIO_H + +#include +#include "oldlist.h" + +/*---------------------------------------------------------------------------- + Public Funtion Prototypes +--------------------------------------------------------------------------*/ +LIST read_list(const char *filename); +#endif diff --git a/cutil/minmax.h b/cutil/minmax.h new file mode 100644 index 0000000000..c1d319a756 --- /dev/null +++ b/cutil/minmax.h @@ -0,0 +1,40 @@ +/****************************************************************************** + ** Filename: minmax.h + ** Purpose: Utility macros for min and max functions. + ** Author: Dan Johnson + ** History: Wed Oct 17 09:54:32 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef MINMAX_H +#define MINMAX_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ + +#ifndef MAX +#define MAX(x,y) (((x)>=(y))?(x):(y)) +#endif + +#ifndef MIN +#define MIN(x,y) (((x)<=(y))?(x):(y)) +#endif + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#endif diff --git a/cutil/oldheap.cpp b/cutil/oldheap.cpp new file mode 100644 index 0000000000..abddf0da19 --- /dev/null +++ b/cutil/oldheap.cpp @@ -0,0 +1,337 @@ +/****************************************************************************** + ** Filename: heap.c + ** Purpose: Routines for managing heaps (smallest at root) + ** Author: Dan Johnson + ** History: 3/13/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "oldheap.h" +#include "freelist.h" +#include "danerror.h" +#include "emalloc.h" +#include + +#define FATHER(N) ((N)>>1) +#define LEFTSON(N) ((N)<<1) +#define RIGHTSON(N) ((N)<<1 + 1) + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +HEAP *MakeHeap(int Size) { +/* + ** Parameters: + ** Size maximum number of entries in the heap + ** Globals: + ** None + ** Operation: + ** This routine creates and initializes a new heap data + ** structure containing Size elements. In actuality, Size + 1 + ** elements are allocated. The first element, element 0, is + ** unused, this makes the index arithmetic easier. + ** Return: + ** Pointer to the new heap. + ** Exceptions: + ** None + ** History: + ** 3/13/89, DSJ, Created. + */ + HEAP *NewHeap; + + NewHeap = (HEAP *) Emalloc (sizeof (HEAP) + Size * sizeof (HEAPENTRY)); + + NewHeap->Size = Size; + NewHeap->FirstFree = 1; + return (NewHeap); +} /* MakeHeap */ + + +/*---------------------------------------------------------------------------*/ +int HeapPop(HEAP *Heap, FLOAT32 *Key, void *out_ptr) { +/* + ** Parameters: + ** Heap ptr to heap whose top is to be removed and returned + ** Key place to put key of top heap item + ** Data place to put data of top heap item + ** Globals: + ** None + ** Operation: + ** This routine removes the top item on the heap and places + ** its contents into Key and Data. + ** Return: + ** OK if top entry returned, EMPTY if heap is empty + ** Exceptions: + ** None + ** History: + ** 5/10/91, DSJ, Created (Modified from GetTopOfHeap). + */ + INT32 Hole; + FLOAT32 HoleKey; + INT32 Son; + void **Data = (void **) out_ptr; + + if (Heap->FirstFree <= 1) + return (EMPTY); + + *Key = Heap->Entry[1].Key; + *Data = Heap->Entry[1].Data; + + Heap->FirstFree--; + + /* imagine the hole at the root is filled with the last entry in the heap */ + HoleKey = Heap->Entry[Heap->FirstFree].Key; + Hole = 1; + + /* while hole has 2 sons */ + while ((Son = LEFTSON (Hole)) < Heap->FirstFree) { + /* find the son with the smallest key */ + if (Heap->Entry[Son].Key > Heap->Entry[Son + 1].Key) + Son++; + + /* if key for hole is greater than key for son, sift hole down */ + if (HoleKey > Heap->Entry[Son].Key) { + Heap->Entry[Hole].Key = Heap->Entry[Son].Key; + Heap->Entry[Hole].Data = Heap->Entry[Son].Data; + Hole = Son; + } + else + break; + } + Heap->Entry[Hole].Key = HoleKey; + Heap->Entry[Hole].Data = Heap->Entry[Heap->FirstFree].Data; + return (OK); +} /* HeapPop */ + + +/********************************************************************** + * HeapPopWorst + * + * Remove the largest item from the heap. + **********************************************************************/ +int HeapPopWorst(HEAP *Heap, FLOAT32 *Key, void *out_ptr) { +/* + ** Parameters: + ** Heap ptr to heap whose top is to be removed and returned + ** Key place to put key of top heap item + ** Data place to put data of top heap item + */ + INT32 Index; /*current index */ + INT32 Hole; + FLOAT32 HoleKey; + INT32 Father; + void *HoleData; + void **Data = (void **) out_ptr; + + if (Heap->FirstFree <= 1) + return (EMPTY); + + HoleKey = Heap->Entry[1].Key; + Hole = 1; + Heap->FirstFree--; + for (Index = Heap->FirstFree, Father = FATHER (Index); Index > Father; + Index--) + if (Heap->Entry[Index].Key > HoleKey) { + /*find biggest */ + HoleKey = Heap->Entry[Index].Key; + Hole = Index; + } + *Key = HoleKey; + *Data = Heap->Entry[Hole].Data; + + HoleKey = Heap->Entry[Heap->FirstFree].Key; + Heap->Entry[Hole].Key = HoleKey; + HoleData = Heap->Entry[Heap->FirstFree].Data; + Heap->Entry[Hole].Data = HoleData; + + /* now sift last entry to its rightful place */ + Father = FATHER (Hole); /*father of hole */ + while (Hole > 1 && Heap->Entry[Father].Key > HoleKey) { + /*swap entries */ + Heap->Entry[Hole].Key = Heap->Entry[Father].Key; + Heap->Entry[Hole].Data = Heap->Entry[Father].Data; + Heap->Entry[Father].Data = HoleData; + Heap->Entry[Father].Key = HoleKey; + Hole = Father; + Father = FATHER (Hole); + } + return (OK); +} /* HeapPop */ + + +/*---------------------------------------------------------------------------*/ +void HeapPush(HEAP *Heap, FLOAT32 Key, void *Data) { +/* + ** Parameters: + ** Heap ptr to heap to store new item in + ** Key numeric key associated with new item + ** Data ptr to data contents of new item + ** Globals: + ** None + ** Operation: + ** This routine stores Data into Heap and associates it + ** with Key. The heap is + ** maintained in such a way that the item with the lowest key + ** is always at the top of the heap. + ** Return: + ** None + ** Exceptions: + ** HEAPFULL error if heap size is exceeded + ** History: + ** 5/10/91, DSJ, Created (Modified version of HeapStore). + */ + INT32 Item; + INT32 Father; + + if (Heap->FirstFree > Heap->Size) + DoError (HEAPFULL, "Heap size exceeded"); + + Item = Heap->FirstFree; + Heap->FirstFree++; + while (Item != 1) { + Father = FATHER (Item); + if (Heap->Entry[Father].Key > Key) { + Heap->Entry[Item].Key = Heap->Entry[Father].Key; + Heap->Entry[Item].Data = Heap->Entry[Father].Data; + Item = Father; + } + else + break; + } + Heap->Entry[Item].Key = Key; + Heap->Entry[Item].Data = Data; +} /* HeapPush */ + + +/*---------------------------------------------------------------------------*/ +void HeapStore(HEAP *Heap, HEAPENTRY *Entry) { +/* + ** Parameters: + ** Heap ptr to heap to store new item in + ** Entry ptr to item to be stored in Heap + ** Globals: + ** None + ** Operation: + ** This routine stores Entry into Heap. The heap is + ** maintained in such a way that the item with the lowest key + ** is always at the top of the heap. + ** Return: + ** None + ** Exceptions: + ** HEAPFULL error if heap size is exceeded + ** History: + ** 3/13/89, DSJ, Created. + */ + INT32 Item; + INT32 Father; + + if (Heap->FirstFree > Heap->Size) + DoError (HEAPFULL, "Heap size exceeded"); + + Item = Heap->FirstFree; + Heap->FirstFree++; + while (Item != 1) { + Father = FATHER (Item); + if (Heap->Entry[Father].Key > Entry->Key) { + Heap->Entry[Item].Key = Heap->Entry[Father].Key; + Heap->Entry[Item].Data = Heap->Entry[Father].Data; + Item = Father; + } + else + break; + } + Heap->Entry[Item].Key = Entry->Key; + Heap->Entry[Item].Data = Entry->Data; +} /* HeapStore */ + + +/*---------------------------------------------------------------------------*/ +int GetTopOfHeap(HEAP *Heap, HEAPENTRY *Entry) { +/* + ** Parameters: + ** Heap ptr to heap whose top is to be removed and returned + ** Entry ptr to heap entry to be filled with top entry on Heap + ** Globals: + ** None + ** Operation: + ** This routine removes the top item on the heap and copies its + ** contents into Entry. + ** Return: + ** OK if top entry returned, EMPTY if heap is empty + ** Exceptions: + ** None + ** History: + ** 3/13/89, DSJ, Created. + */ + INT32 Hole; + FLOAT32 HoleKey; + INT32 Son; + + if (Heap->FirstFree <= 1) + return (EMPTY); + + Entry->Key = Heap->Entry[1].Key; + Entry->Data = Heap->Entry[1].Data; + + Heap->FirstFree--; + + /* imagine the hole at the root is filled with the last entry in the heap */ + HoleKey = Heap->Entry[Heap->FirstFree].Key; + Hole = 1; + + /* while hole has 2 sons */ + while ((Son = LEFTSON (Hole)) < Heap->FirstFree) { + /* find the son with the smallest key */ + if (Heap->Entry[Son].Key > Heap->Entry[Son + 1].Key) + Son++; + + /* if key for hole is greater than key for son, sift hole down */ + if (HoleKey > Heap->Entry[Son].Key) { + Heap->Entry[Hole].Key = Heap->Entry[Son].Key; + Heap->Entry[Hole].Data = Heap->Entry[Son].Data; + Hole = Son; + } + else + break; + } + Heap->Entry[Hole].Key = HoleKey; + Heap->Entry[Hole].Data = Heap->Entry[Heap->FirstFree].Data; + return (OK); +} /* GetTopOfHeap */ + + +/*---------------------------------------------------------------------------*/ +void FreeHeapData(HEAP *Heap, void_dest destructor) { +/* + ** Parameters: + ** Heap heap whose data is to be freed + ** Deallocator function to be used to deallocate data + ** Globals: none + ** Operation: This routine is similar to FreeHeap in that it + ** deallocates the memory consumed by the heap. However, it + ** also calls Deallocator for each item in the heap so that + ** this data is also deallocated. + ** Return: none + ** Exceptions: none + ** History: Tue May 15 08:52:04 1990, DSJ, Created. + */ + HEAPENTRY Entry; + + while (GetTopOfHeap (Heap, &Entry) != EMPTY) + destructor (Entry.Data); + + FreeHeap(Heap); +} /* FreeHeapData */ diff --git a/cutil/oldheap.h b/cutil/oldheap.h new file mode 100644 index 0000000000..8a98d00a78 --- /dev/null +++ b/cutil/oldheap.h @@ -0,0 +1,126 @@ +/****************************************************************************** + ** Filename: heap.h + ** Purpose: Definition of heap access routines. + ** Author: Dan Johnson + ** History: 3/13/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef HEAP_H +#define HEAP_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "general.h" +#include "cutil.h" + +#define HEAPFULL 3000 + +#define OK 0 +#define EMPTY -1 + +typedef struct +{ + FLOAT32 Key; + void *Data; +} + + +HEAPENTRY; + +typedef struct +{ + INT32 Size; + INT32 FirstFree; + HEAPENTRY Entry[1]; +} + + +HEAP; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +#define FreeHeap(H) memfree(H) +#define MaxSizeOfHeap(H) (H->Size) +#define SizeOfHeap(H) (H->FirstFree - 1) +#define InitHeap(H) (H->FirstFree = 1) +#define HeapFull(H) ((H)->FirstFree > (H)->Size) +#define HeapEmpty(H) ((H)->FirstFree <= 1) + +/* macros for accessing elements in heap by index. The indicies vary from + 0 to SizeOfHeap-1. No bounds checking is done. Elements accessed in + this manner are in random order relative to the Key values. These + macros should never be used as the LHS of an assignment statement as this + will corrupt the heap.*/ +#define HeapKeyFor(H,E) ((H)->Entry[(E)+1].Key) +#define HeapDataFor(H,E) ((H)->Entry[(E)+1].Data) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +HEAP *MakeHeap(int Size); + +int HeapPop(HEAP *Heap, FLOAT32 *Key, void *out_ptr); + +int HeapPopWorst(HEAP *Heap, FLOAT32 *Key, void *out_ptr); + +void HeapPush(HEAP *Heap, FLOAT32 Key, void *Data); + +void HeapStore(HEAP *Heap, HEAPENTRY *Entry); + +int GetTopOfHeap(HEAP *Heap, HEAPENTRY *Entry); + +void FreeHeapData(HEAP *Heap, void_dest destructor); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* heap.c +HEAP *MakeHeap + _ARGS((int Size)); + +int HeapPop + _ARGS((HEAP *Heap, + FLOAT32 *Key, + char **Data)); + +int HeapPopWorst + _ARGS((HEAP *Heap, + FLOAT32 *Key, + char **Data)); + +void HeapPush + _ARGS((HEAP *Heap, + FLOAT32 Key, + char *Data)); + +void HeapStore + _ARGS((HEAP *Heap, + HEAPENTRY *Entry)); + +int GetTopOfHeap + _ARGS((HEAP *Heap, + HEAPENTRY *Entry)); + +void FreeHeapData + _ARGS((HEAP *Heap, + void (*Deallocator )())); + +#undef _ARGS +*/ +#endif diff --git a/cutil/oldlist.cpp b/cutil/oldlist.cpp new file mode 100644 index 0000000000..3e5adc7d7b --- /dev/null +++ b/cutil/oldlist.cpp @@ -0,0 +1,393 @@ +/* -*-C-*- +############################################################################### +# +# File: list.c +# Description: List processing procedures. +# Author: Mark Seaman, Software Productivity +# Created: Thu Jul 23 13:24:09 1987 +# Modified: Thu Dec 22 10:59:52 1988 (Mark Seaman) marks@hpgrlt +# Language: C +# Package: N/A +# Status: Reusable Software Component +# +# (c) Copyright 1987, Hewlett-Packard Company. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +# +################################################################################ + +* Revision 1.13 90/03/06 15:37:54 15:37:54 marks (Mark Seaman) +* Look for correct file of or +* +* Revision 1.12 90/02/26 17:37:36 17:37:36 marks (Mark Seaman) +* Added pop_off and join_on +* + + This file contains a set of general purpose list manipulation routines. + These routines can be used in a wide variety of ways to provide several + different popular data structures. A new list can be created by declaring + a variable of type 'LIST', and can be initialized with the value 'NIL'. + All of these routines check for the NIL condition before dereferencing + pointers. NOTE: There is a users' manual available in printed form from + Mark Seaman at (303) 350-4492 at Greeley Hard Copy. + + To implement a STACK use: + + push to add to the Stack l = push (l, (LIST) "jim"); + pop to remove items from the Stack l = pop (l); + first to access the head name = (char *) first (l); + + To implement a QUEUE use: + + push_last to add to the Queue l = push_last (l, (LIST) "jim"); + pop remove items from the Queue l = pop (l); + first to access the head name = (char *) first (l); + + To implement LISP like functions use: + + first CAR x = (int) first (l); + rest CDR l = rest (l); + push CONS l = push (l, (LIST) this); + last LAST x = last (l); + concat APPEND l = concat (r, s); + count LENGTH x = count (l); + search MEMBER if (search (l, x, NULL)) + + To implement SETS use: + + adjoin l = adjoin (l, x); + set_union l = set_union (r, s); + intersection l = intersection (r, s); + set_difference l = set_difference (r, s); + delete l = delete (s, x, NULL); + search if (search (l, x, NULL)) + + To Implement Associated LISTS use: + + lpush l = lpush (l, p); + assoc s = assoc (l, x); + adelete l = adelete (l, x); + + The following rules of closure exist for the functions provided. + a = first (push (a, b)) + b = rest (push (a, b)) + a = push (pop (a), a)) For all a <> NIL + a = reverse (reverse (a)) + +******************************************************************************/ +#include "oldlist.h" +#include "structures.h" +#include +#if MAC_OR_DOS +#include +#else +#include "freelist.h" +#endif + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +#define add_on(l,x) l = push (l,first (x)) +#define next_one(l) l = rest (l) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * c o u n t + * + * Recursively count the elements in a list. Return the count. + **********************************************************************/ +int count(LIST var_list) { + int temp = 0; + + iterate (var_list) temp += 1; + return (temp); +} + + +/********************************************************************** + * d e l e t e d + * + * Delete all the elements out of the current list that match the key. + * This operation destroys the original list. The caller will supply a + * routine that will compare each node to the + * key, and return a non-zero value when they match. If the value + * NULL is supplied for is_equal, the is_key routine will be used. + **********************************************************************/ +LIST delete_d(LIST list, void *key, int_compare is_equal) { + LIST result = NIL; + LIST last_one = NIL; + + if (is_equal == NULL) + is_equal = is_same; + + while (list != NIL) { + if (!(*is_equal) (first (list), key)) { + if (last_one == NIL) { + last_one = list; + list = rest (list); + result = last_one; + set_rest(last_one, NIL); + } + else { + set_rest(last_one, list); + last_one = list; + list = rest (list); + set_rest(last_one, NIL); + } + } + else { + list = pop (list); + } + } + return (result); +} + + +/********************************************************************** + * d e s t r o y + * + * Return the space taken by a list to the heap. + **********************************************************************/ +LIST destroy(LIST list) { + LIST next; + + while (list != NIL) { + next = rest (list); + free_cell(list); + list = next; + } + return (NIL); +} + + +/********************************************************************** + * d e s t r o y n o d e s + * + * Return the space taken by the LISTs of a list to the heap. + **********************************************************************/ +void destroy_nodes(LIST list, void_dest destructor) { + if (destructor == NULL) + destructor = memfree; + + while (list != NIL) { + (*destructor) (first (list)); + list = pop (list); + } +} + + +/********************************************************************** + * i n s e r t + * + * Create a list element and rearange the pointers so that the first + * element in the list is the second aurgment. + **********************************************************************/ +void insert(LIST list, void *node) { + LIST element; + + if (list != NIL) { + element = push (NIL, node); + set_rest (element, rest (list)); + set_rest(list, element); + node = first (list); + list->node = first (rest (list)); + list->next->node = (LIST) node; + } +} + + +/********************************************************************** + * i s s a m e n o d e + * + * Compare the list node with the key value return TRUE (non-zero) + * if they are equivalent strings. (Return FALSE if not) + **********************************************************************/ +int is_same_node(void *item1, void *item2) { + return (item1 == item2); +} + + +/********************************************************************** + * i s s a m e + * + * Compare the list node with the key value return TRUE (non-zero) + * if they are equivalent strings. (Return FALSE if not) + **********************************************************************/ +int is_same(void *item1, void *item2) { + return (!strcmp ((char *) item1, (char *) item2)); +} + + +/********************************************************************** + * j o i n + * + * Join the two lists together. This function is similar to concat + * except that concat creates a new list. This function returns the + * first list updated. + **********************************************************************/ +LIST join(LIST list1, LIST list2) { + if (list1 == NIL) + return (list2); + set_rest (last (list1), list2); + return (list1); +} + + +/********************************************************************** + * l a s t + * + * Return the last list item (this is list type). + **********************************************************************/ +LIST last(LIST var_list) { + while (rest (var_list) != NIL) + var_list = rest (var_list); + return (var_list); +} + + +/********************************************************************** + * n t h c e l l + * + * Return nth list cell in the list. + **********************************************************************/ +void *nth_cell(LIST var_list, int item_num) { + int x = 0; + iterate(var_list) { + if (x++ == item_num) + return (var_list); + } + return (var_list); +} + + +/********************************************************************** + * p o p + * + * Return the list with the first element removed. Destroy the space + * that it occupied in the list. + **********************************************************************/ +LIST pop(LIST list) { + LIST temp; + + temp = rest (list); + + if (list != NIL) { + free_cell(list); + } + return (temp); +} + + +/********************************************************************** + * p u s h + * + * Create a list element. Push the second parameter (the node) onto + * the first parameter (the list). Return the new list to the caller. + **********************************************************************/ +LIST push(LIST list, void *element) { + LIST t; + + t = new_cell (); + t->node = (LIST) element; + set_rest(t, list); + return (t); +} + + +/********************************************************************** + * p u s h l a s t + * + * Create a list element. Add the element onto the end of the list. + **********************************************************************/ +LIST push_last(LIST list, void *item) { + LIST t; + + if (list != NIL) { + t = last (list); + t->next = push (NIL, item); + return (list); + } + else + return (push (NIL, item)); +} + + +/********************************************************************** + * r e v e r s e + * + * Create a new list with the elements reversed. The old list is not + * destroyed. + **********************************************************************/ +LIST reverse(LIST list) { + LIST newlist = NIL; + + iterate (list) copy_first (list, newlist); + return (newlist); +} + + +/********************************************************************** + * r e v e r s e d + * + * Create a new list with the elements reversed. The old list is + * destroyed. + **********************************************************************/ +LIST reverse_d(LIST list) { + LIST result = reverse (list); + destroy(list); + return (result); +} + + +/********************************************************************** + * s a d j o i n + * + * Adjoin an element to an assorted list. The original list is + * modified. Returns the modified list. + **********************************************************************/ +LIST s_adjoin(LIST var_list, void *variable, int_compare compare) { + LIST l; + int result; + + if (compare == NULL) + compare = (int_compare) strcmp; + + l = var_list; + iterate(l) { + result = (*compare) (variable, first (l)); + if (result == 0) + return (var_list); + else if (result < 0) { + insert(l, variable); + return (var_list); + } + } + return (push_last (var_list, variable)); +} + + +/********************************************************************** + * s e a r c h + * + * Search list, return NIL if not found. Return the list starting from + * the item if found. The compare routine "is_equal" is passed in as + * the third paramter to this routine. If the value NULL is supplied + * for is_equal, the is_key routine will be used. + **********************************************************************/ +LIST search(LIST list, void *key, int_compare is_equal) { + if (is_equal == NULL) + is_equal = is_same; + + iterate (list) if ((*is_equal) (first (list), key)) + return (list); + return (NIL); +} diff --git a/cutil/oldlist.h b/cutil/oldlist.h new file mode 100644 index 0000000000..68692b206f --- /dev/null +++ b/cutil/oldlist.h @@ -0,0 +1,350 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: list.h (Formerly list.h) + * Description: List processing procedures declarations. + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Wed Dec 5 15:43:17 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + * + * This file contains the interface for a set of general purpose list + * manipulation routines. For the implementation of these routines see + * the file "list.c". + * + ******************************************************************************** + * + * INDEX + * ======= + * + * BASICS: + * ------- + * first - Macro to return the first list node (not the cell). + * rest - Macro the return the second list cell + * pop - Destroy one list cell + * push - Create one list cell and set the node and next fields + * + * ITERATION: + * ----------------- + * iterate - Macro to create a for loop to visit each cell. + * iterate_list - Macro to visit each cell using a local variable. + * for_each - Applies a function to each node. + * + * LIST CELL COUNTS: + * ----------------- + * count - Returns the number of list cells in the list. + * second - Returns the second node. + * third - Returns the third node. + * fourth - Returns the fourth node. + * fifth - Returns the fifth node. + * last - Returns the last list cell. + * pair - Creates a list of two elements. + * + * COPYING: + * ----------------- + * copy_first - Pushes the first element from list 1 onto list 2. + * copy - Create a copy of a list. + * concat - Creates a new list that is a copy of both input lists. + * delete_n - Creates a new list without the chosen elements. + * reverse - Creates a backwards copy of the input list. + * sort - Use quick sort to construct a new list. + * transform - Creates a new list by transforming each of the nodes. + * + * TRANFORMS: (Note: These functions all modify the input list.) + * ---------- + * join - Concatenates list 1 and list 2. + * delete_d - Removes the requested elements from the list. + * transform_d - Modifies the list by applying a function to each node. + * insert - Add a new element into this spot in a list. (not NIL) + * push_last - Add a new element onto the end of a list. + * reverse_d - Reverse a list and destroy the old one. + * + * ASSOCIATED LISTS: + * ----------------- + * adelete - Remove a particular entry from an associated list. + * assoc - Find an entry in an associated list that matches a key. + * match - Return the data element of an a-list entry. + * + * DISPLAY: + * ----------------- + * print_cell - Print a hex dump of a list cell. + * show - Displays a string and a list (using lprint). + * + * SETS: + * ----- + * adjoin - Add a new element to list if it does not exist already. + * intersection - Create a new list that is the set intersection. + * set_union - Create a new list that is the set intersection. + * set_difference - Create a new list that is the set difference. + * s_adjoin - Add an element to a sort list if it is not there. + * s_intersection - Set intersection on a sorted list. Modifies old list. + * s_union - Set intersection on a sorted list. Modifies old list. + * search - Return the pointer to the list cell whose node matches. + * + * COMPARISONS: + * ----------------- + * is_same - Compares each node to the key. + * is_not_same - Compares each node to the key. + * is_key - Compares first of each node to the key. + * is_not_key - Compares first of each node to the key. + * + * CELL OPERATIONS: + * ----------------- + * new_cell - Obtain a new list cell from the free list. Allocate. + * free_cell - Return a list cell to the free list. + * destroy - Return all list cells in a list. + * destroy_nodes - Apply a function to each list cell and destroy the list. + * set_node - Assign the node field in a list cell. + * set_rest - Assign the next field in a list cell. + * + ***********************************************************************/ + +#ifndef LIST_H +#define LIST_H + +#include "cutil.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define NIL (LIST) 0 +typedef struct list_rec +{ + struct list_rec *node; + struct list_rec *next; +} _LIST_; +typedef _LIST_ *LIST; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/* Predefinitions */ +#define rest(l) ((l) ? (l)->next : NIL) +#define first(l) ((l) ? (l)->node : NIL) + +/********************************************************************** + * c o p y f i r s t + * + * Do the appropriate kind a push operation to copy the first node from + * one list to another. + * + **********************************************************************/ + +#define copy_first(l1,l2) \ +(l2=push(l2, first(l1))) + +/********************************************************************** + * i t e r a t e + * + * Visit each node in the list. Replace the old list with the list + * minus the head. Continue until the list is NIL. + **********************************************************************/ + +#define iterate(l) \ +for (; (l) != NIL; (l) = rest (l)) + +/********************************************************************** + * i t e r a t e l i s t + * + * Visit each node in the list (l). Use a local variable (x) to iterate + * through all of the list cells. This macro is identical to iterate + * except that it does not lose the original list. + **********************************************************************/ + +#define iterate_list(x,l) \ +for ((x)=(l); (x)!=0; (x)=rest(x)) + +/********************************************************************** + * j o i n o n + * + * Add another list onto the tail of this one. The list given as an input + * parameter is modified. + **********************************************************************/ + +#define JOIN_ON(list1,list2) \ +((list1) = join ((list1), (list2))) + +/********************************************************************** + * p o p o f f + * + * Add a cell onto the front of a list. The list given as an input + * parameter is modified. + **********************************************************************/ + +#define pop_off(list) \ +((list) = pop (list)) + +/********************************************************************** + * p u s h o n + * + * Add a cell onto the front of a list. The list given as an input + * parameter is modified. + **********************************************************************/ + +#define push_on(list,thing) \ +((list) = push (list, (LIST) (thing))) + +/********************************************************************** + * s e c o n d + * + * Return the contents of the second list element. + * + * #define second(l) first (rest (l)) + **********************************************************************/ + +#define second(l) \ +first (rest (l)) + +/********************************************************************** + * s e t r e s t + * + * Change the "next" field of a list element to point to a desired place. + * + * #define set_rest(l,node) l->next = node; + **********************************************************************/ + +#define set_rest(l,cell)\ +((l)->next = (cell)) + +/********************************************************************** + * t h i r d + * + * Return the contents of the third list element. + * + * #define third(l) first (rest (rest (l))) + **********************************************************************/ + +#define third(l) \ +first (rest (rest (l))) + +/*---------------------------------------------------------------------- + Public Funtion Prototypes +----------------------------------------------------------------------*/ +int count(LIST var_list); + +LIST delete_d(LIST list, void *key, int_compare is_equal); + +LIST destroy(LIST list); + +void destroy_nodes(LIST list, void_dest destructor); + +void insert(LIST list, void *node); + +int is_same_node(void *item1, void *item2); + +int is_same(void *item1, void *item2); + +LIST join(LIST list1, LIST list2); + +LIST last(LIST var_list); + +void *nth_cell(LIST var_list, int item_num); + +LIST pop(LIST list); + +LIST push(LIST list, void *element); + +LIST push_last(LIST list, void *item); + +LIST reverse(LIST list); + +LIST reverse_d(LIST list); + +LIST s_adjoin(LIST var_list, void *variable, int_compare compare); + +LIST search(LIST list, void *key, int_compare is_equal); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif + +typedef void (*destructor) _ARGS((LIST l)); + +typedef LIST (*list_proc) _ARGS((LIST a)); + +int count +_ARGS((LIST var_list)); + +LIST delete_d +_ARGS((LIST list, + LIST key, + int_compare is_equal)); + +LIST destroy +_ARGS((LIST list)); + +LIST destroy_nodes +_ARGS((LIST list, + void_dest destructor)); + +void insert +_ARGS((LIST list, + LIST node)); + +int is_same_node +_ARGS((LIST s1, + LIST s2)); + +int is_same +_ARGS((LIST s1, + LIST s2)); + +LIST join +_ARGS((LIST list1, + LIST list2)); + +LIST last +_ARGS((LIST var_list)); + +LIST nth_cell +_ARGS((LIST var_list, + int item_num)); + +LIST pop +_ARGS((LIST list)); + +LIST push +_ARGS((LIST list, + LIST element)); + +LIST push_last +_ARGS((LIST list, + LIST item)); + +LIST reverse +_ARGS((LIST list)); + +LIST reverse_d +_ARGS((LIST list)); + +LIST s_adjoin +_ARGS((LIST var_list, + LIST variable, + int_compare compare)); + +LIST search +_ARGS((LIST list, + LIST key, + int_compare is_equal)); + +#undef _ARGS +*/ +#endif diff --git a/cutil/structures.cpp b/cutil/structures.cpp new file mode 100644 index 0000000000..d2d155c7cf --- /dev/null +++ b/cutil/structures.cpp @@ -0,0 +1,66 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: structures.c (Formerly structures.c) + * Description: Allocate all the different types of structures. + * Author: Mark Seaman, OCR Technology + * Created: Wed May 30 10:27:26 1990 + * Modified: Mon Jul 15 10:39:18 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "structures.h" +#include "callcpp.h" + +#include + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +#define BLOBBLOCK 64 /*no allocated together */ +#define OUTLINEBLOCK 300 /*of each type */ +#define NODEBLOCK 36 /*blocks all about 1K bytes */ +#define EDGEPTBLOCK 50 +#define WERDBLOCK 42 +#define LISTBLOCK 300 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +int structblockcount = 0; +void_void memory_print_functions[NUM_DATA_TYPES]; +int max_data_types = 0; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +makestructure (newword, oldword, printword, TWERD, +freeword, WERDBLOCK, "TWERD", wordcount) +makestructure (newoutline, oldoutline, printol, TESSLINE, +freeoutline, OUTLINEBLOCK, "TESSLINE", outlinecount); + +makestructure (new_cell, free_cell, printcell, _LIST_, +freelist, LISTBLOCK, "LIST", listcount); + +newstructure (newblob, TBLOB, freeblob, BLOBBLOCK, "newblob", blobcount); +oldstructure (oldblob, TBLOB, freeblob, "BLOB", blobcount); + +newstructure (newedgept, EDGEPT, freeedgept, EDGEPTBLOCK, "newedgept", +edgeptcount); +oldstructure (oldedgept, EDGEPT, freeedgept, "EDGEPT", edgeptcount); diff --git a/cutil/structures.h b/cutil/structures.h new file mode 100644 index 0000000000..ef666fc46f --- /dev/null +++ b/cutil/structures.h @@ -0,0 +1,112 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: structures.h (Formerly structures.h) + * Description: Allocate all the different types of structures. + * Author: Mark Seaman, OCR Technology + * Created: Wed May 30 10:12:12 1990 + * Modified: Tue May 21 11:07:47 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef STRUCTURES_H +#define STRUCTURES_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "tessclas.h" +#include "oldlist.h" +#include "freelist.h" +#include "danerror.h" + +#define NUM_DATA_TYPES 20 + +extern int max_data_types; +extern void_void memory_print_functions[NUM_DATA_TYPES]; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * makestructure + * + * Allocate a chunk of memory for a particular data type. This macro + * defines an allocation, deallocation, and status printing function + * for each new data type. + **********************************************************************/ + +#define makestructure(newfunc,old,print,type,nextfree,blocksize,typestring,usecount) \ +type *newfunc() \ +{ \ + return new type; \ +} \ + \ + \ + \ +void old(type* deadelement) \ +{ \ + delete deadelement; \ +} \ + + +/********************************************************************** + * newstructure + * + * Allocate a chunk of memory for a particular data type. + **********************************************************************/ + +#define newstructure(name,type,nextfree,blocksize,errorstring,usecount)\ +type *name() /*returns a new type*/\ +{\ + return new type;\ +} + +/********************************************************************** + * oldstructure + * + * Returns a structure to the freelist + **********************************************************************/ + +#define oldstructure(name,type,nextfree,stringtype,usecount)\ +\ +type *name(type* deadelement)\ +{\ + type *returnelement; /*return next ptr*/\ +\ + returnelement=deadelement->next; /*return link*/\ + delete deadelement; \ + return returnelement;\ +} + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +extern TBLOB *newblob(); +extern TBLOB *oldblob(TBLOB *); + +extern TESSLINE *newoutline(); +extern void oldoutline(TESSLINE *); + +extern EDGEPT *newedgept(); +extern EDGEPT *oldedgept(EDGEPT *); + +extern TWERD *newword(); +extern void oldword(TWERD *); + +extern LIST new_cell(); +extern void free_cell(LIST); +#endif diff --git a/cutil/tessarray.cpp b/cutil/tessarray.cpp new file mode 100644 index 0000000000..944c42596c --- /dev/null +++ b/cutil/tessarray.cpp @@ -0,0 +1,115 @@ +/* -*-C-*- +################################################################################ +# +# File: array.c +# Description: Dynamic Array of Strings +# Author: Mark Seaman, Software Productivity +# Created: Thu Jul 23 13:24:09 1987 +# Modified: Wed Mar 6 15:18:33 1991 (Mark Seaman) marks@hpgrlt +# Language: C +# Package: N/A +# Status: Reusable Software Component +# +# (c) Copyright 1987, Hewlett-Packard Company. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +# +################################################################################ + +This file contains the implentations of a set of dynamic array of string +manipulation routines. For the interface definitions and documentation +of these routines see the file "das.h". + +***************************************************************************/ + +#include "tessarray.h" +#include "callcpp.h" +#include "freelist.h" + +#include +#include +#ifdef __MSW32__ +#include +#endif +#include +#if MAC_OR_DOS +#include +#endif + +/********************************************************************** + * array_insert + * + * Insert a data element into a particular spot in the array. Move all + * the elements in the array (past that spot) down one to make room for + * the new element. + **********************************************************************/ +ARRAY array_insert(ARRAY array, int index, void *value) { + int x; + + array = array_push (array, NULL); + for (x = array_count (array) - 1; x > index; x--) + array_value (array, x) = array_value (array, x - 1); + array_value (array, index) = value; + return (array); +} + + +/********************************************************************** + * array_new + * + * Create a new array with a certain number of elements. If the number + * of elements requested is 0 then the default number will be used. + **********************************************************************/ +ARRAY array_new(int num) { + ARRAY temp; + int x; + + if (num == 0) + num = DEFAULT_SIZE; + temp = (ARRAY) memalloc ((num - 2) * sizeof (char *) + + sizeof (struct array_record)); + if (!temp) { + cprintf ("error: Out of memory in array_new\n"); + exit (1); //?err_exit (); + } + array_count (temp) = 0; + array_limit (temp) = num; + for (x = 0; x < num; x++) + array_value (temp, x) = (char *) 0; + return (temp); +} + + +/********************************************************************** + * array_push + * + * Add a new element onto the top of the array. If there is not room + * more room is made by "realloc"ing the array. This means that the + * new array location may change. All previous references to its old + * location may no longer be valid. + **********************************************************************/ +ARRAY array_push(ARRAY array, void *value) { + if (array_count (array) == array_limit (array)) { + array = (ARRAY) memrealloc (array, (array_limit (array) * 2 - 2) * + sizeof (char *) + + sizeof (struct array_record), + (array_limit (array) - + 2) * sizeof (char *) + + sizeof (struct array_record)); + if (!array) { + cprintf ("error: Out of memory in array_push\n"); + exit (1); //?err_exit (); + } + array_limit (array) *= 2; + } + array_count (array)++; + array_top (array) = value; + return (array); +} diff --git a/cutil/tessarray.h b/cutil/tessarray.h new file mode 100644 index 0000000000..8b5a38cead --- /dev/null +++ b/cutil/tessarray.h @@ -0,0 +1,166 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: array.h (Formerly array.h) + * Description: Dynamic Array of String + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon Sep 24 14:15:59 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ***************************************************************************** + +This file contains a set of general purpose dynamic array of string routines. +These routines can be used in a wide variety of ways to provide several +different popular data structures. A new "das" can be created by declaring +a variable of type 'DAS' +******************************************************************************/ + +#ifndef ARRAY_H +#define ARRAY_H + +/* +---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------- +*/ + +#include + +/* +---------------------------------------------------------------------- + T y p e s +---------------------------------------------------------------------- +*/ + +typedef struct array_record +{ + size_t limit; + size_t top; + void *base[2]; +} *ARRAY; + +typedef void (*voidProc) (); + +typedef int (*intProc) (); + +/* +---------------------------------------------------------------------- + M a c r o s +---------------------------------------------------------------------- +*/ + +#define DEFAULT_SIZE 2 + +/********************************************************************** + * array_count + * + * Return the value of the number of elements currently in the array. + **********************************************************************/ + +#define array_count(a) \ +((a)->top) + +/********************************************************************** + * array_free + * + * Free the memory allocated to this array. + **********************************************************************/ + +#define array_free \ +memfree + +/********************************************************************** + * array_index + * + * Check to make sure that the index value is valid. Return the + * value of the nth element currently in the array. + **********************************************************************/ + +#define array_index(a,i) \ +((ibase[i] : 0) + +/********************************************************************** + * array_limit + * + * Return the maximum number of elements that could be currently held + * in this array without further expansion. + **********************************************************************/ + +#define array_limit(a) \ +((a)->limit) + +/********************************************************************** + * array_loop + * + * Iterate through each of the array elements. Each value can then be + * accessed by: + * array_index (a, x) + **********************************************************************/ + +#define array_loop(a,x) \ +for (x=0; x < array_count (a); x++) + +/********************************************************************** + * array_top + * + * Return the last element that was pushed on this array. + **********************************************************************/ + +#define array_top(a) \ +((a)->base[array_count (a) - 1]) + +/********************************************************************** + * array_value + * + * Return the nth element of the array. Don't do range checking. + **********************************************************************/ + +#define array_value(a,i) \ +((a)->base[i]) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +ARRAY array_insert(ARRAY array, int index, void *value); + +ARRAY array_new(int num); + +ARRAY array_push(ARRAY array, void *value); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* array.c +ARRAY array_insert + _ARGS((ARRAY array, + int index, + char *value)); + +ARRAY array_new + _ARGS((int num)); + +ARRAY array_push + _ARGS((ARRAY array, + char *value)); + +#undef _ARGS +*/ +#endif diff --git a/cutil/tordvars.cpp b/cutil/tordvars.cpp new file mode 100644 index 0000000000..7ef1bbc95e --- /dev/null +++ b/cutil/tordvars.cpp @@ -0,0 +1,95 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: tovars.c (Formerly to-vars.c) + * Description: Text Ordering Control Variables + * Author: Mark Seaman, OCR Technology + * Created: Wed Jan 17 12:47:29 1990 + * Modified: Tue Jul 30 16:22:40 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "mfcpch.h" +#include "debug.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +FILE *rawfile; /* Text before dictionary */ +FILE *textfile; /* Text output file */ +FILE *matcher_fp; //matcher log +FILE *correct_fp; //correct text + +int write_output; /* Text file output */ +int write_raw_output; /* Text before context */ + +int similarity_enable = 0; +int similarity_debug = 0; + +make_float_var (certainty_threshold, -2.25, make_certainty_threshold, +4, 5, set_certainty_value, "Certainty Value"); + +make_int_var (num_word_choices, 30, make_num_word_choices, +4, 6, set_num_choices, "Number of choices"); + +make_toggle_var (blob_skip, 0, make_blob_skip, +4, 7, toggle_skip, "Skip to Next selection"); + +make_float_var (overlap_threshold, 0.33, make_overlap_threshold, +9, 7, set_overlap, "Overlap Threshold"); + +make_toggle_var (debug_3, 0, make_debug_3, 6, 3, toggle_debug_3, "Debug #3"); + +make_toggle_var (debug_5, 0, make_debug_5, 6, 5, toggle_debug_5, "Debug #5"); + +make_toggle_var (debug_8, 0, make_debug_8, 6, 8, toggle_debug_8, "Debug #8"); + +make_toggle_var (display_ratings, 0, make_display_ratings, +6, 9, toggle_ratings, "Ratings display"); + +make_toggle_var (display_text, 1, make_display_text, +6, 10, toggle_text, "Display Text"); + +make_toggle_var (show_bold, 1, make_show_bold, +6, 17, set_show_bold, "Show Bold Text"); + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * init_textord_vars + * + * Create variables to be used by the texord section of code. + **********************************************************************/ +void init_textord_vars() { + int_variable (write_output, "write_output", 0); + int_variable (write_raw_output, "write_raw_output", 0); + make_certainty_threshold(); + make_num_word_choices(); + make_blob_skip(); + make_overlap_threshold(); + make_show_bold(); + + make_debug_3(); + make_debug_5(); + make_debug_8(); + + make_display_ratings(); + make_display_text(); +} diff --git a/cutil/tordvars.h b/cutil/tordvars.h new file mode 100644 index 0000000000..130b83f799 --- /dev/null +++ b/cutil/tordvars.h @@ -0,0 +1,60 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: tovars.h (Formerly to-vars.h) + * Description: Do word classification + * Author: Mark Seaman, OCR Technology + * Created: Wed Oct 25 16:33:01 1989 + * Modified: Mon Jul 1 14:28:23 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef TOVARS_H +#define TOVARS_H + +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int debug_3; +extern int debug_5; +extern int debug_8; + +extern FILE *rawfile; /* Text before dictionary */ +extern FILE *textfile; /* Text output file */ +extern FILE *correct_fp; //correct text +extern FILE *matcher_fp; + +extern int blob_skip; /* Skip to next selection */ +extern int num_word_choices; /* How many words to keep */ +extern int similarity_enable; /* Switch for Similarity */ +extern int similarity_debug; /* Level of debug output */ +extern int write_raw_output; /* Text before context */ +extern int write_output; /* Text file output */ +extern int display_ratings; /* Show the ratings */ +extern int show_bold; /* Use bold text */ +extern int display_text; /* Show word text */ +extern int display_blocks; /* Show word as boxes */ + +extern float overlap_threshold; /* Overlap Threshold */ +extern float certainty_threshold;/* When to quit looking */ + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void init_textord_vars(); +#endif diff --git a/cutil/variables.cpp b/cutil/variables.cpp new file mode 100644 index 0000000000..2a40b811b8 --- /dev/null +++ b/cutil/variables.cpp @@ -0,0 +1,310 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: variables.c (Formerly variables.c) + * Description: Variable handler for control flags + * Author: Mark Seaman, OCR Technology + * Created: Tue Dec 12 09:03:49 1989 + * Modified: Thu Apr 4 11:01:37 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include +#include + +#include "variables.h" +#include "callcpp.h" +#include "listio.h" +#include "globals.h" +#include "scanutils.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static LIST variable_list = NIL; +//extern char *demodir; + +VALUE dummy; + +/*---------------------------------------------------------------------- + Macros +----------------------------------------------------------------------*/ +/********************************************************************** + * scan_int + * + * Scan in an integer value from a string. It might be in decimal or + * hex. Read it into "integer". + **********************************************************************/ + +#define scan_int(stripped,integer) \ +if (stripped [2] == 'x') \ + sscanf (&stripped[3], "%x", &integer); \ +else \ + sscanf (&stripped[1], "%d", &integer) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ + +void free_variables() { + destroy_nodes(variable_list, free); + variable_list = NIL; +} +/********************************************************************** + * add_ptr_variable + * + * Add a new ptr variable to the global variable list. Initalize its + * value. + **********************************************************************/ +void add_ptr_variable(void *address, + const char *string, + VALUE default_value, + variables_io reader, + variables_io writer) { + VARIABLE *this_var; + this_var = (VARIABLE *) malloc (sizeof (VARIABLE)); + + this_var->address = address; + this_var->string = string; + this_var->default_value = default_value; + this_var->type_reader = reader; + this_var->type_writer = writer; + + *((void **) this_var->address) = default_value.ptr_part; + variable_list = push (variable_list, this_var); +} + + +/********************************************************************** + * add_32bit_variable + * + * Add a new 32bit variable to the global variable list. Initalize + * its value. + **********************************************************************/ +void add_32bit_variable(void *address, + const char *string, + VALUE default_value, + variables_io reader, + variables_io writer) { + VARIABLE *this_var; + this_var = (VARIABLE *) malloc (sizeof (VARIABLE)); + + this_var->address = address; + this_var->string = string; + this_var->default_value = default_value; + this_var->type_reader = reader; + this_var->type_writer = writer; + + *((int *) this_var->address) = default_value.int_part; + variable_list = push (variable_list, this_var); +} + + +/********************************************************************** + * float_read + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void float_read(VARIABLE *variable, char *string) { + float f; + + #ifdef EMBEDDED + // We have no sscanf with float functionality here + *((float *) variable->address) = strtofloat(strip_line (string)); + #else + sscanf (strip_line (string), "%f", &f); + *((float *) variable->address) = f; + #endif +} + + +/********************************************************************** + * float_write + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void float_write(VARIABLE *variable, char *string) { + float *f; + f = (float *) variable->address; + sprintf (string, "%s\t%4.2f", variable->string, *f); +} + + +/********************************************************************** + * int_read + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void int_read(VARIABLE *variable, char *string) { + char *stripped; + int integer; + + stripped = strip_line (string); + /* Add the value */ + if (stripped[0] == '+') { + scan_int(stripped, integer); + *((int *) variable->address) += integer; + } + else if (stripped[0] == '|') { + scan_int(stripped, integer); + *((int *) variable->address) = integer | *((int *) variable->address); + } /* Subtract the value */ + else if (stripped[0] == '_') { + scan_int(stripped, integer); + *((int *) variable->address) = (~integer) & + *((int *) variable->address); + } + else { + /* Set the value */ + if (stripped[1] == 'x') { + sscanf (&stripped[2], "%x", &integer); + } + else { + sscanf (stripped, "%d", &integer); + } + *((int *) variable->address) = integer; + } +} + + +/********************************************************************** + * int_write + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void int_write(VARIABLE *variable, char *string) { + sprintf (string, "%s\t%d", variable->string, *((int *) variable->address)); +} + + +/********************************************************************** + * read_variables + * + * Read a file that contains assignments for all the desired variables. + * This type of file can be written using the function write_variables. + **********************************************************************/ +void read_variables(const char *filename) { + int x = 0; + char *this_string; + LIST var_strings; + char name[1024]; + FILE *fp; + VARIABLE *this_var; + /* Read the strings */ + if (filename == NULL || filename[0] == '\0') + return; + + strcpy(name, demodir); + strcat (name, "tessdata/tessconfigs/"); + strcat(name, filename); + if ((fp = fopen (name, "r")) == NULL) + strcpy(name, filename); + else + fclose(fp); + var_strings = read_list (name); + iterate(var_strings) { + /* Get the name string */ + this_string = (char *) first (var_strings); + if (this_string[0] != '#') { + for (x = 0; + x < strlen (this_string) && this_string[x] != ' ' + && this_string[x] != '\t'; x++); + this_string[x] = '\0'; + /* Find variable record */ + this_var = (VARIABLE *) first (search (variable_list, this_string, + same_var_name)); + if (this_var == 0) { + cprintf ("error: Could not find variable '%s'\n", this_string); + exit (1); //?err_exit (); + } + /* Read the value */ + this_string[x] = '\t'; + (*(this_var->type_reader)) (this_var, this_string); + } + } +} + + +/********************************************************************** + * same_var_name + * + * Return TRUE if the VARIABLE has the name string in it. + **********************************************************************/ +int same_var_name(void *item1, //VARIABLE *variable, + void *item2) { //char *string) + VARIABLE *variable; + char *string; + + variable = (VARIABLE *) item1; + string = (char *) item2; + + if (strcmp (variable->string, string)) + return (FALSE); + else + return (TRUE); +} + + +/********************************************************************** + * string_read + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void string_read(VARIABLE *variable, char *string) { + char *value; + + value = strsave (strip_line (string)); + *((char **) variable->address) = value; +} + + +/********************************************************************** + * string_write + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void string_write(VARIABLE *variable, char *string) { + sprintf (string, "%s\t%s", variable->string, + *((char **) variable->address)); +} + + +/********************************************************************** + * strip_line + * + * Remove the name off the front of the line and strip the white space + * before and after the value. + **********************************************************************/ +char *strip_line(char *string) { + int x; + int y; + + /* Skip over name */ + for (x = 0; + x < strlen (string) && string[x] != '\t' && string[x] != ' '; x++); + /* Skip over whitespace */ + while (x < strlen (string) && (string[x] == '\t' || string[x] == ' ')) + x++; + /* Strip trailing whitespace */ + for (y = strlen (string); + y >= 0 && (string[y - 1] == '\t' || string[y - 1] == ' '); y--) + string[y] = '\0'; + /* Return result */ + return (&string[x]); +} diff --git a/cutil/variables.h b/cutil/variables.h new file mode 100644 index 0000000000..02de6ab583 --- /dev/null +++ b/cutil/variables.h @@ -0,0 +1,168 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: variables.h (Formerly variables.h) + * Description: Variable handler for control flags + * Author: Mark Seaman, OCR Technology + * Created: Tue Dec 12 09:03:49 1989 + * Modified: Fri Jan 12 16:57:36 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef VARIABLES_H +#define VARIABLES_H + +#include "cutil.h" +#include "oldlist.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef union +{ + float float_part; + int int_part; + void *ptr_part; + const char *char_part; +} VALUE; + +class VARIABLE; + +typedef void (*variables_io) (VARIABLE * variable, char *string); + +class VARIABLE +{ + public: + void *address; + const char *string; + VALUE default_value; + variables_io type_reader; + variables_io type_writer; +}; + +extern VALUE dummy; /* Needed for macros */ + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/* To hide variable names for relase to UNLV DONT put the variable names in + the executable: Toggle be either defining or not defining SECURE_NAMES*/ +/* #define SECURE_NAMES defined in secnames.h when necessary */ +#ifdef SECURE_NAMES +/********************************************************************** + * float_variable + * + * Create a floating point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define float_variable(name,string,default) \ +dummy.float_part = default; \ +add_32bit_variable (&name, "", dummy, float_read, float_write) + +/********************************************************************** + * string_variable + * + * Create a string point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define string_variable(name,string,default) \ +dummy.char_part = default; \ +add_ptr_variable (&name, "", dummy, string_read, string_write) + +/********************************************************************** + * int_variable + * + * Create a int point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define int_variable(name,string,default) \ +dummy.int_part = default; \ +add_32bit_variable (&name, "", dummy, int_read, int_write) + +#else +/********************************************************************** + * float_variable + * + * Create a floating point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define float_variable(name,string,default) \ +dummy.float_part = default; \ +add_32bit_variable (&name, string, dummy, float_read, float_write) + +/********************************************************************** + * string_variable + * + * Create a string point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define string_variable(name,string,default) \ +dummy.char_part = default; \ +add_ptr_variable (&name, string, dummy, string_read, string_write) + +/********************************************************************** + * int_variable + * + * Create a int point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define int_variable(name,string,default) \ +dummy.int_part = default; \ +add_32bit_variable (&name, string, dummy, int_read, int_write) +#endif + +/*-------------------------------------------------------------------------- + Public Function Prototoypes +----------------------------------------------------------------------------*/ +void free_variables(); + +void add_ptr_variable(void *address, + const char *string, + VALUE default_value, + variables_io reader, + variables_io writer); + +void add_32bit_variable(void *address, + const char *string, + VALUE default_value, + variables_io reader, + variables_io writer); + +void float_read(VARIABLE *variable, char *string); + +void float_write(VARIABLE *variable, char *string); + +void int_read(VARIABLE *variable, char *string); + +void int_write(VARIABLE *variable, char *string); + +void read_variables(const char *filename); + +int same_var_name(void *item1, //VARIABLE *variable, + void *item2); //char *string) + +void string_read(VARIABLE *variable, char *string); + +void string_write(VARIABLE *variable, char *string); + +char *strip_line(char *string); +#endif diff --git a/dict/Makefile.am b/dict/Makefile.am new file mode 100644 index 0000000000..582c5504e5 --- /dev/null +++ b/dict/Makefile.am @@ -0,0 +1,11 @@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + choicearr.h choices.h context.h dawg.h hyphen.h matchdefs.h \ + permdawg.h permnum.h permute.h states.h stopper.h trie.h + +noinst_LIBRARIES = libtesseract_dict.a +libtesseract_dict_a_SOURCES = \ + choices.cpp context.cpp dawg.cpp hyphen.cpp permdawg.cpp \ + permnum.cpp permute.cpp states.cpp stopper.cpp trie.cpp diff --git a/dict/Makefile.in b/dict/Makefile.in new file mode 100644 index 0000000000..95e0d1282c --- /dev/null +++ b/dict/Makefile.in @@ -0,0 +1,539 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = dict +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_dict_a_AR = $(AR) $(ARFLAGS) +libtesseract_dict_a_LIBADD = +am_libtesseract_dict_a_OBJECTS = choices.$(OBJEXT) context.$(OBJEXT) \ + dawg.$(OBJEXT) hyphen.$(OBJEXT) permdawg.$(OBJEXT) \ + permnum.$(OBJEXT) permute.$(OBJEXT) states.$(OBJEXT) \ + stopper.$(OBJEXT) trie.$(OBJEXT) +libtesseract_dict_a_OBJECTS = $(am_libtesseract_dict_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_dict_a_SOURCES) +DIST_SOURCES = $(libtesseract_dict_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil +EXTRA_DIST = \ + choicearr.h choices.h context.h dawg.h hyphen.h matchdefs.h \ + permdawg.h permnum.h permute.h states.h stopper.h trie.h + +noinst_LIBRARIES = libtesseract_dict.a +libtesseract_dict_a_SOURCES = \ + choices.cpp context.cpp dawg.cpp hyphen.cpp permdawg.cpp \ + permnum.cpp permute.cpp states.cpp stopper.cpp trie.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu dict/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu dict/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_dict.a: $(libtesseract_dict_a_OBJECTS) $(libtesseract_dict_a_DEPENDENCIES) + -rm -f libtesseract_dict.a + $(libtesseract_dict_a_AR) libtesseract_dict.a $(libtesseract_dict_a_OBJECTS) $(libtesseract_dict_a_LIBADD) + $(RANLIB) libtesseract_dict.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/choices.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dawg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hyphen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/permdawg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/permnum.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/permute.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/states.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stopper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trie.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/dict/choicearr.h b/dict/choicearr.h new file mode 100644 index 0000000000..c573b6345e --- /dev/null +++ b/dict/choicearr.h @@ -0,0 +1,96 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: choicearr.h (Formerly choicearr.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Mar 19 15:27:49 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + */ + +#ifndef CHOICEARR_H +#define CHOICEARR_H + +/* +---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------- +*/ + +#include "tessarray.h" +#include "choices.h" + +/* +---------------------------------------------------------------------- + T y p e s +---------------------------------------------------------------------- +*/ + +typedef ARRAY CHOICES_LIST; + +#define CHOICES_PER_LIST 40 + +/* +---------------------------------------------------------------------- + M a c r o s +---------------------------------------------------------------------- +*/ + +/********************************************************************** + * free_choice_list + * + * Free a list of choices. Free the array structure but not each of the + * sublists of choices. + **********************************************************************/ + +#define free_choice_list(choice_list) \ +array_free (choice_list) + +/********************************************************************** + * for_each_choice + * + * Iterate through each of the possible choices. + **********************************************************************/ + +#define for_each_choice(array,index) \ +array_loop (array, index) + +/********************************************************************** + * free_all_choices + * + * Free an array of choices (deep free). + **********************************************************************/ + +#define free_all_choices(choices,index) \ +for_each_choice (choices, index) { \ + free_choices ((CHOICES) array_value (choices, index)); \ +} \ +array_free (choices) \ + + +/********************************************************************** + * new_choice_list + * + * Return a new array structure that is a list of choices. Each set of + * choices will be of type CHOICES. + **********************************************************************/ + +#define new_choice_list() \ +array_new (CHOICES_PER_LIST) +#endif diff --git a/dict/choices.cpp b/dict/choices.cpp new file mode 100644 index 0000000000..7440d5d41e --- /dev/null +++ b/dict/choices.cpp @@ -0,0 +1,161 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: choices.c (Formerly choices.c) + * Description: Handle the new ratings choices for Wise Owl + * Author: Mark Seaman, OCR Technology + * Created: Fri Sep 22 14:05:51 1989 + * Modified: Wed May 22 14:12:34 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#include "choices.h" +#include "structures.h" +#include "tordvars.h" +#include "callcpp.h" +#include "danerror.h" +#include "host.h" + +/*---------------------------------------------------------------------- + Variables +------------------------------------------------------------------------*/ +#define CHOICEBLOCK 100 /* Cells per block */ + +makestructure (newchoice, oldchoice, printchoice, A_CHOICE, +freechoice, CHOICEBLOCK, "A_CHOICE", choicecount) +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * append_choice + * + * Create a new choice record. Store the string value in a safe place. + * Add the new choice record to the list. + * + * NB - This is only used by matchers, so permuter is always NO_PERM + * SPC 16/9/92 + **********************************************************************/ +CHOICES append_choice(CHOICES ratings, + const char *string, + float rating, + float certainty, + INT8 config) { + A_CHOICE *this_choice; + + this_choice = new_choice (string, rating, certainty, config, NO_PERM); + ratings = push_last (ratings, (LIST) this_choice); + return (ratings); +} + + +/********************************************************************** + * copy_choices + * + * Copy a list of choices. This means that there will be two copies + * in memory. + **********************************************************************/ +CHOICES copy_choices(CHOICES choices) { + CHOICES l; + CHOICES result = NIL; + + iterate_list(l, choices) { + result = push (result, + (LIST) new_choice (class_string (first (l)), + class_probability (first (l)), + class_certainty (first (l)), + class_config (first (l)), + class_permuter (first (l)))); + } + return (reverse_d (result)); +} + + +/********************************************************************** + * free_choice + * + * Free up the memory taken by one choice rating. + **********************************************************************/ +void free_choice(void *arg) { //LIST choice) + A_CHOICE *this_choice; + LIST choice = (LIST) arg; + + this_choice = (A_CHOICE *) choice; + if (this_choice) { + if (this_choice->string) + strfree (this_choice->string); + oldchoice(this_choice); + } +} + + +/********************************************************************** + * new_choice + * + * Create a new choice record. Store the string value in a safe place. + **********************************************************************/ +A_CHOICE *new_choice(const char *string, + float rating, + float certainty, + INT8 config, + char permuter) { + A_CHOICE *this_choice; + + this_choice = newchoice (); + this_choice->string = strsave (string); + this_choice->rating = rating; + this_choice->certainty = certainty; + this_choice->config = config; + this_choice->permuter = permuter; + return (this_choice); +} + + +/********************************************************************** + * print_choices + * + * Print the probability ratings for a particular blob or word. + **********************************************************************/ +void print_choices( /* List of (A_CHOICE*) */ + const char *label, + CHOICES rating) { + int first_one = TRUE; + char str[CHARS_PER_LINE]; + int len; + + cprintf ("%-20s\n", label); + if (rating == NIL) + cprintf (" No rating "); + + iterate(rating) { + + if (first_one && show_bold) { + cprintf ("|"); + len = sprintf (str, " %s ", best_string (rating)); + print_bold(str); + while (len++ < 8) + cprintf (" "); + } + else { + cprintf ("| %-7s", best_string (rating)); + } + + cprintf ("%5.2lf ", best_probability (rating)); + + cprintf ("%5.2lf", best_certainty (rating)); + first_one = FALSE; + } + cprintf ("\n"); +} diff --git a/dict/choices.h b/dict/choices.h new file mode 100644 index 0000000000..7e7b588888 --- /dev/null +++ b/dict/choices.h @@ -0,0 +1,191 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: choices.h (Formerly choices.h) + * Description: Handle the new ratings choices for Wise Owl + * Author: Mark Seaman, OCR Technology + * Created: Fri Sep 22 14:05:51 1989 + * Modified: Fri Jan 4 12:04:01 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + * + * FUNCTIONS TO CALL + * ----------------- + * append_choice - Create a new choice and add it to the list. + * class_probability - Return the probability of a given character class. + * class_string - Return the string corresponding to a character choice. + * free_choice - Free up the memory taken by one choice rating. + * new_choice - Create one choice record one set up the fields. + * + *********************************************************************************/ + +#ifndef CHOICES_H +#define CHOICES_H + +#include +#include + +#include "oldlist.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef LIST CHOICES; /* CHOICES */ +//typedef float PROBABILITY; /* PROBABILITY */ +//typedef char PERM_TYPE; /* PERMUTER CODE */ + +/* permuter codes used in A_CHOICEs for words */ + +#define NO_PERM 0 +#define TOP_CHOICE_PERM 1 +#define LOWER_CASE_PERM 2 +#define UPPER_CASE_PERM 3 +#define NUMBER_PERM 4 +#define SYSTEM_DAWG_PERM 5 +#define DOC_DAWG_PERM 6 +#define USER_DAWG_PERM 7 +#define FREQ_DAWG_PERM 8 +#define COMPOUND_PERM 9 + +typedef struct choicestruct +{ /* A_CHOICE */ + float rating; + float certainty; + char permuter; + INT8 config; + char *string; +} A_CHOICE; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * best_string + * + * Return the string corresponding to the best choice. + **********************************************************************/ +#define best_string(choices) \ +(first (choices) ? ((A_CHOICE*) (first (choices)))->string : NULL) + +/********************************************************************** + * best_probability + * + * Return the probability of the best choice. + **********************************************************************/ +#define best_probability(choices) \ +(((A_CHOICE*) (first (choices)))->rating) + +/********************************************************************** + * best_certainty + * + * Return the certainty of the best choice. + **********************************************************************/ +#define best_certainty(choices) \ +(((A_CHOICE*) (first (choices)))->certainty) + +/********************************************************************** + * class_probability + * + * Return the probability of a given character class. + **********************************************************************/ +#define class_probability(choice) \ +(((A_CHOICE*) (choice))->rating) + +/********************************************************************** + * class_certainty + * + * Return the certainty of a given character class. + **********************************************************************/ +#define class_certainty(choice) \ +(((A_CHOICE*) (choice))->certainty) + +/********************************************************************** + * class_string + * + * Return the probability of a given character class. + **********************************************************************/ +#define class_string(choice) \ +(((A_CHOICE*) (choice))->string) + +/********************************************************************** + * class_permuter + * + * Return the permuter of a given character class. + **********************************************************************/ +#define class_permuter(choice) \ +(((A_CHOICE*) (choice))->permuter) + +/********************************************************************** + * class_config + * + * Return the config of a given character class. + **********************************************************************/ +#define class_config(choice) \ +(((A_CHOICE*) (choice))->config) + +/********************************************************************** + * clone_choice + * + * Copy the contents of this choice record onto another replacing any + * previous value it might of had. + **********************************************************************/ +#define clone_choice(choice_2,choice_1) \ +if (class_string (choice_2)) strfree (class_string (choice_2)); \ +class_probability (choice_2) = class_probability (choice_1); \ +class_certainty (choice_2) = class_certainty (choice_1); \ +class_permuter (choice_2) = class_permuter (choice_1); \ +class_string (choice_2) = strsave (class_string (choice_1)) \ + + +/********************************************************************** + * free_choices + * + * Free a list of choices. + **********************************************************************/ +#define free_choices(c) \ +destroy_nodes ((c), free_choice) + +/********************************************************************** + * print_bold + * + * Print a string in bold type by using escape sequences. This only + * works for certain output devices. + **********************************************************************/ +#define print_bold(string) \ + cprintf ("\033&dB%s\033&d@", string) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +CHOICES append_choice(CHOICES ratings, + const char *string, + float rating, + float certainty, + INT8 config); + +CHOICES copy_choices(CHOICES choices); + +void free_choice(void *arg); //LIST choice); + +A_CHOICE *new_choice(const char *string, + float rating, + float certainty, + INT8 config, + char permuter); + +void print_choices(const char *label, CHOICES rating); +#endif diff --git a/dict/context.cpp b/dict/context.cpp new file mode 100644 index 0000000000..20920dc4fe --- /dev/null +++ b/dict/context.cpp @@ -0,0 +1,225 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: context.c (Formerly context.c) + * Description: Context checking functions + * Author: Mark Seaman, OCR Technology + * Created: Thu Feb 15 11:18:24 1990 + * Modified: Tue Jul 9 17:38:16 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#include "context.h" +#include "tordvars.h" +#include "callcpp.h" + +#include +#include +#include +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static FILE *choice_file = NULL; /* File to save choices */ + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * close_choices + * + * Close the choices file. + **********************************************************************/ +void close_choices() { + if (choice_file) + fclose(choice_file); +} + + +/********************************************************************** + * fix_quotes + * + * Fix up two single quote to make them two double quotes. + **********************************************************************/ +void fix_quotes(char *str) { + int i; + for (i = 0; i < strlen (str); i++) { + + if (((str[i] == '\'') || (str[i] == '`')) && + ((str[i + 1] == '\'') || (str[i + 1] == '`'))) { + str[i] = '\"'; + strcpy (str + i + 1, str + i + 2); + } + } +} + + +/********************************************************************** + * punctuation_ok + * + * Check a string to see if it matches a set of punctuation rules. + **********************************************************************/ +int punctuation_ok(const char *word) { + int punctuation_types[5]; + int trailing = 0; + int num_puncts = 0; + register int x; + register char ch; + + for (x = 0; x < 5; x++) + punctuation_types[x] = 0; + + for (x = 0; x < strlen (word); x++) { + + if (isalpha (word[x])) { + if (trailing && + !(isalpha (word[x - 1]) || + (word[x - 1] == '\'' && + (word[x] == 's' || word[x] == 'd' || word[x] == 'l')) || + (word[x - 1] == '-'))) + return (-1); + trailing = 1; + } + else { + ch = word[x]; + + if (ch == '.' && trailing) { + if (punctuation_types[0]) + return (-1); + (punctuation_types[0])++; + } + + else if (((ch == '{') || (ch == '[') || (ch == '(')) && !trailing) { + if (punctuation_types[1]) + return (-1); + (punctuation_types[1])++; + } + + else if (((ch == '}') || (ch == ']') || (ch == ')')) && trailing) { + if (punctuation_types[2]) + return (-1); + (punctuation_types[2])++; + } + + else if (((ch == ':') || + (ch == ';') || + (ch == '!') || + (ch == '-') || (ch == ',') || (ch == '?')) && trailing) { + if (punctuation_types[3]) + return (-1); + (punctuation_types[3])++; + if (ch == '-') + punctuation_types[3] = 0; + } + + else if ((ch == '`') || (ch == '\"') || (ch == '\'')) { + if ((word[x + 1] == '`') || (word[x + 1] == '\'')) { + x++; + } + (punctuation_types[4])++; + if (punctuation_types[4] > 2) + return (-1); + } + + else if (!isdigit (ch)) + return (-1); + } + } + + for (x = 0; x < 5; x++) { + if (punctuation_types[x]) + num_puncts++; + } + + return (num_puncts); +} + + +/********************************************************************** + * case_ok + * + * Check a string to see if it matches a set of lexical rules. + **********************************************************************/ +int case_ok(const char *word) { + static int case_state_table[6][4] = { { + /* 0. Begining of word */ + /* P U L D */ + /* -1. Error on case */ + 0, 1, 5, 4 + }, + { /* 1. After initial capital */ + 0, 3, 2, 4 + }, + { /* 2. After lower case */ + 0, -1, 2, -1 + }, + { /* 3. After upper case */ + 0, 3, -1, 4 + }, + { /* 4. After a digit */ + 0, -1, -1, 4 + }, + { /* 5. After initial lower case */ + 5, -1, 2, -1 + }, + }; + + register int last_state = 0; + register int state = 0; + register int x; + + for (x = 0; x < strlen (word); x++) { + + if (islower (word[x])) + state = case_state_table[state][2]; + else if (isupper (word[x])) + state = case_state_table[state][1]; + else if (isdigit (word[x])) + state = case_state_table[state][3]; + else + state = case_state_table[state][0]; + + if (debug_3) + cprintf ("Case state = %d, char = %c\n", state, word[x]); + + if (state == -1) { + /* Handle ACCRONYMs */ + if (word[x] == 's' && + !isalpha (word[x + 1]) && !isdigit (word[x + 1])) + state = last_state; + else + return (FALSE); + } + + last_state = state; + } + return state != 5; /*single lower is bad */ +} + + +/********************************************************************** + * write_choice_line + * + * Write a blank line to the choices file. This will indicate that + * there is a new word that is following. + **********************************************************************/ +void write_choice_line() { + if (choice_file) { + fprintf (choice_file, "\n"); + fflush(choice_file); + } +} diff --git a/dict/context.h b/dict/context.h new file mode 100644 index 0000000000..abf98810de --- /dev/null +++ b/dict/context.h @@ -0,0 +1,70 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: context.h (Formerly context.h) + * Description: Context checking functions + * Author: Mark Seaman, OCR Technology + * Created: Thu Feb 15 11:18:24 1990 + * Modified: Tue Jul 9 17:00:38 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + */ + +#ifndef CONTEXT_H +#define CONTEXT_H + +#include "choices.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void close_choices(); + +void fix_quotes(char *str); + +int punctuation_ok(const char *word); + +int case_ok(const char *word); + +void write_choice_line(); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* context.c +void close_choices + _ARGS((void)); + +void fix_quotes + _ARGS((char *str)); + +int punctuation_ok + _ARGS((char *word)); + +int case_ok + _ARGS((char *word)); + +void write_choice_line + _ARGS((void)); + +#undef _ARGS +*/ +#endif diff --git a/dict/dawg.cpp b/dict/dawg.cpp new file mode 100644 index 0000000000..ba0e147d40 --- /dev/null +++ b/dict/dawg.cpp @@ -0,0 +1,366 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: dawg.c (Formerly dawg.c) + * Description: Use a Directed Accyclic Word Graph + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Wed Jul 24 16:59:16 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "dawg.h" +#include "cutil.h" +#include "callcpp.h" +#include "context.h" +#include "strngs.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +INT32 debug = 0; +INT32 case_sensative = 0; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * edge_char_of + * + * Return the edge that corresponds to the letter out of this node. + **********************************************************************/ +EDGE_REF edge_char_of(EDGE_ARRAY dawg, + NODE_REF node, + int character, + int word_end) { + EDGE_REF edge = node; + + if (! case_sensative) character = tolower (character); + + if (edge_occupied (dawg, edge)) { + do { + if ((edge_letter (dawg, edge) == character) && + (! word_end || end_of_word(dawg,edge))) + return (edge); + + } edge_loop (dawg, edge); + } + + return (NO_EDGE); +} + + +/********************************************************************** + * edges_in_node + * + * Count the number of edges in this node in the DAWG. This includes + * both forward and back links. + **********************************************************************/ +INT32 edges_in_node(EDGE_ARRAY dawg, NODE_REF node) { + EDGE_REF edge = node; + + if (edge_occupied (dawg, edge)) { + edge_loop(dawg, edge); + if (edge_occupied (dawg, edge) && backward_edge (dawg, edge)) { + edge_loop(dawg, edge); + return (edge - node); + } + else { + return (edge - node); + } + } + else { + return (edge - node); + } +} + + +/********************************************************************** + * letter_is_okay + * + * Check this letter in light of the current state. If everything is + * still OK then return TRUE; + **********************************************************************/ +INT32 letter_is_okay(EDGE_ARRAY dawg, + NODE_REF *node, + INT32 char_index, + char prevchar, + const char *word, + INT32 word_end) { + EDGE_REF edge; + STRING dummy_word(word); // Auto-deleting string fixes memory leak. + + if (*node == NO_EDGE) { /* Trailing punctuation */ + if (trailing_punc (dummy_word [char_index]) + && (!trailing_punc (prevchar) + || punctuation_ok(dummy_word.string())>=0)) + return (TRUE); + else + return (FALSE); + } + else { + /* Leading punctuation */ + if (*node == 0 && + char_index != 0 && + isalpha (dummy_word [char_index]) && + ! leading_punc (dummy_word [char_index-1]) && + dummy_word [char_index-1] != '-') { + return (FALSE); + } + } + /* Handle compund words */ + if (dummy_word [char_index] == '-') { + if (char_index>0 && !word_end + && word [char_index-1] == '-' + && word [char_index+1] == '-') + return FALSE; /*not allowed*/ + dummy_word [char_index] = (char) 0; + if (word_in_dawg (dawg, dummy_word.string())) { + dummy_word [char_index] = '-'; + *node = 0; + return (TRUE); + } + else { + dummy_word [char_index] = '-'; + return (FALSE); + } + } + /* Check the DAWG */ + edge = edge_char_of (dawg, *node, dummy_word [char_index], word_end); + + if (edge != NO_EDGE) { /* Normal edge in DAWG */ + if (case_sensative || case_is_okay (dummy_word, char_index)) { + //next_node (dawg, edge); + *node = (dawg)[edge] & NO_EDGE; + return (TRUE); + } + else { + return (FALSE); + } + } + else { + /* Leading punctuation */ + if (leading_punc (word [char_index]) && + (char_index == 0 || leading_punc (dummy_word [char_index-1]))) { + *node = 0; + if (leading_punc (prevchar) || punctuation_ok (word)>=0) + return (TRUE); + else + return FALSE; + } + /* Trailing punctuation */ + if (verify_trailing_punct (dawg, &dummy_word[0], char_index)) { + *node = NO_EDGE; + return (TRUE); + } + + return (FALSE); + } +} + + +/********************************************************************** + * num_forward_edges + * + * Count and return the number of forward edges for this node. + **********************************************************************/ +INT32 num_forward_edges(EDGE_ARRAY dawg, NODE_REF node) { + EDGE_REF edge = node; + INT32 num = 0; + + if (forward_edge (dawg, edge)) { + do { + num++; + } edge_loop (dawg, edge); + } + + return (num); +} + + +/********************************************************************** + * print_dawg_node + * + * Print the contents of one of the nodes in the DAWG. + **********************************************************************/ +void print_dawg_node(EDGE_ARRAY dawg, NODE_REF node) { + EDGE_REF edge = node; + const char *forward_string = "FORWARD"; + const char *backward_string = " "; + + const char *last_string = "LAST"; + const char *not_last_string = " "; + + const char *eow_string = "EOW"; + const char *not_eow_string = " "; + + const char *direction; + const char *is_last; + const char *eow; + + char ch; + + if (edge_occupied (dawg, edge)) { + do { + if (forward_edge (dawg, edge)) direction = forward_string; + else direction = backward_string; + + if (last_edge (dawg, edge)) is_last = last_string; + else is_last = not_last_string; + + if (end_of_word (dawg, edge)) eow = eow_string; + else eow = not_eow_string; + + ch = edge_letter (dawg, edge); + cprintf ("%7d : next = %7d, char = '%c', %s %s %s\n", + (int) edge, (int) next_node (dawg, edge), ch, + direction, is_last, eow); + + if (edge - node > MAX_NODE_EDGES) return; + } edge_loop (dawg, edge); + + if (edge_occupied (dawg, edge) && backward_edge (dawg, edge)) { + do { + if (forward_edge (dawg, edge)) direction = forward_string; + else direction = backward_string; + + if (last_edge (dawg, edge)) is_last = last_string; + else is_last = not_last_string; + + if (end_of_word (dawg, edge)) eow = eow_string; + else eow = not_eow_string; + + ch = edge_letter (dawg, edge); + cprintf ("%7d : next = %7d, char = '%c', %s %s %s\n", + (int) edge, (int) next_node (dawg, edge), ch, + direction, is_last, eow); + + if (edge - node > MAX_NODE_EDGES) return; + } edge_loop (dawg, edge); + } + } + else { + cprintf ("%5d : no edges in this node\n", node); + } + new_line(); +} + + +/********************************************************************** + * read_squished_dawg + * + * Write the DAWG out to a file + **********************************************************************/ +void read_squished_dawg(char *filename, EDGE_ARRAY dawg, INT32 max_num_edges) { + FILE *file; + EDGE_REF edge; + INT32 num_edges; + INT32 node_count = 0; + + if (debug) print_string ("read_debug"); + + clear_all_edges(dawg, edge, max_num_edges); + + #ifdef __UNIX__ + file = open_file (filename, "r"); + #else + file = open_file (filename, "rb"); + #endif + fseek(file, 0, SEEK_END); + long fsize = ftell(file); + rewind(file); + fread (&num_edges, sizeof (int), 1, file); + // Auto-detect relative endianness of file and OS as future DAWG + // files may be little-endian. + long diff1 = sizeof(EDGE_RECORD)*num_edges + sizeof(int) - fsize; + reverse32(&num_edges); + long diff2 = sizeof(EDGE_RECORD)*num_edges + sizeof(int) - fsize; + reverse32(&num_edges); + // One of diff1 and diff2 should now be 0, but find the smallest + // just in case. + if (diff1 < 0) diff1 = -diff1; + if (diff2 < 0) diff2 = -diff2; + bool swap = diff2 < diff1; + if (swap) + reverse32(&num_edges); + fread (&dawg[0], sizeof (EDGE_RECORD), num_edges, file); + fclose(file); + if (swap) + for (edge=0;edge +#include "general.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define MAX_WERD_LENGTH (INT32) 40 +#define MAX_NODE_EDGES (INT32) 100 +#define LAST_FLAG (INT32) 1 +#define DIRECTION_FLAG (INT32) 2 +#define WERD_END_FLAG (INT32) 4 + +#define FLAG_START_BIT 21 +#define LETTER_START_BIT 24 + +#define NO_EDGE (INT32) 0x1fffff + +typedef UINT32 EDGE_RECORD; +typedef EDGE_RECORD *EDGE_ARRAY; +typedef INT32 EDGE_REF; +typedef INT32 NODE_REF; + +/*--------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern INT32 case_sensative; +extern INT32 debug; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * next_node + * + * The next node visited in the DAWG by following this edge. + **********************************************************************/ + +#define next_node(edges,e) \ +((edges)[e] & NO_EDGE) + +/********************************************************************** + * set_next_edge + * + * Set the next node link for this edge in the DAWG. + **********************************************************************/ + +#define set_next_edge(edges,e,value) \ +((edges)[e] = ((edges)[e] & (INT32) 0xffe00000) |\ + (value & NO_EDGE)) + +/********************************************************************** + * set_empty_edge + * + * Return TRUE if this edge spot in this location is unoccupied. + **********************************************************************/ + +#define set_empty_edge(edges,e) \ +((edges)[e] = NO_EDGE) + +/********************************************************************** + * clear_all_edges + * + * Go through all the edges in the DAWG and clear out each one. + **********************************************************************/ + +#define clear_all_edges(dawg,edge,max_num_edges) \ +for (edge=0; edge> LETTER_START_BIT) + +/********************************************************************** + * last_edge + * + * Return TRUE if this edge is the last edge in the sequence. This is + * TRUE for the last one in both the forward and backward part. + **********************************************************************/ + +#define last_edge(edges,e) \ +((edges)[e] & (LAST_FLAG << FLAG_START_BIT)) + +/********************************************************************** + * end_of_word + * + * Return TRUE if this edge marks the end of a word. + **********************************************************************/ + +#define end_of_word(edges,e) \ +((edges)[e] & (WERD_END_FLAG << FLAG_START_BIT)) + +/********************************************************************** + * forward_edge + * + * Return TRUE if this edge is in the forward direction. + **********************************************************************/ + +#define forward_edge(edges,e) \ +((edges)[e] & (DIRECTION_FLAG << FLAG_START_BIT) && \ + edge_occupied (edges,e)) + +/********************************************************************** + * backward_edge + * + * Return TRUE if this edge is in the backward direction. + **********************************************************************/ + +#define backward_edge(edges,e) \ +(! ((edges)[e] & (DIRECTION_FLAG << FLAG_START_BIT)) && \ + edge_occupied (edges,e)) + +/********************************************************************** + * edge_loop + * + * Loop for each of the edges in the forward direction. This macro + * can be used in the following way: + *********************************************************************/ + +#define edge_loop(edges,e) \ +while (! last_edge (edges,e++)) + +/********************************************************************** + * case_is_okay + * + * Check the case of this character in the character string to make + * sure that there is not a problem with the case. + **********************************************************************/ + +#define case_is_okay(word,i) \ +(i ? \ + ((isupper(word[i]) && islower(word[i-1])) ? \ + FALSE : \ + ((islower(word[i]) && isupper(word[i-1]) && \ + i>1 && isalpha (word[i-2])) ? \ + FALSE : \ + TRUE)) : \ + TRUE) + +/********************************************************************** + * trailing_punc + * + * Check for leading punctuation. + **********************************************************************/ + +#define trailing_punc(ch) \ +((ch == '}' ) || \ + (ch == ':' ) || \ + (ch == ';' ) || \ + (ch == '-' ) || \ + (ch == ']' ) || \ + (ch == '!' ) || \ + (ch == '?' ) || \ + (ch == '`' ) || \ + (ch == ',' ) || \ + (ch == '.' ) || \ + (ch == ')' ) || \ + (ch == '\"' ) || \ + (ch == '\'' )) + +/********************************************************************** + * leading_punc + * + * Check for leading punctuation. + **********************************************************************/ + +#define leading_punc(ch) \ +((ch == '\"' ) || \ + (ch == '(' ) || \ + (ch == '{' ) || \ + (ch == '[' ) || \ + (ch == '`' ) || \ + (ch == '\'' )) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +EDGE_REF edge_char_of(EDGE_ARRAY dawg, + NODE_REF node, + int character, + int word_end); + +INT32 edges_in_node(EDGE_ARRAY dawg, NODE_REF node); + +INT32 letter_is_okay(EDGE_ARRAY dawg, + NODE_REF *node, + INT32 char_index, + char prevchar, + const char *word, + INT32 word_end); + +INT32 num_forward_edges(EDGE_ARRAY dawg, NODE_REF node); + +void print_dawg_node(EDGE_ARRAY dawg, NODE_REF node); + +void read_squished_dawg(char *filename, EDGE_ARRAY dawg, INT32 max_num_edges); + +INT32 verify_trailing_punct(EDGE_ARRAY dawg, char *word, INT32 char_index); + +INT32 word_in_dawg(EDGE_ARRAY dawg, const char *string); + +/* +#if defined(__STDC__) || defined(__cplusplus) || MAC_OR_DOS +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* dawg.c +EDGE_REF edge_char_of + _ARGS((EDGE_ARRAY dawg, + NODE_REF node, + int character, + int word_end)); + +INT32 edges_in_node + _ARGS((EDGE_ARRAY dawg, + NODE_REF node)); + +INT32 letter_is_okay + _ARGS((EDGE_ARRAY dawg, + NODE_REF *node, + INT32 char_index, + char *word, + INT32 word_end)); + +INT32 num_forward_edges + _ARGS((EDGE_ARRAY dawg, + NODE_REF node)); + +void print_dawg_node + _ARGS((EDGE_ARRAY dawg, + NODE_REF node)); + +void read_squished_dawg + _ARGS((char *filename, + EDGE_ARRAY dawg, + INT32 max_num_edges)); + +INT32 verify_trailing_punct + _ARGS((EDGE_ARRAY dawg, + char *word, + INT32 char_index)); + +INT32 word_in_dawg + _ARGS((EDGE_ARRAY dawg, + char *string)); + +#undef _ARGS +*/ +#endif diff --git a/dict/hyphen.cpp b/dict/hyphen.cpp new file mode 100644 index 0000000000..1fcc2e8a8b --- /dev/null +++ b/dict/hyphen.cpp @@ -0,0 +1,70 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: hyphen.c (Formerly hyphen.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Thu Mar 14 11:09:43 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "const.h" +#include "hyphen.h" +#include "tordvars.h" +#include "callcpp.h" +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +int last_word_on_line = 0; +char *hyphen_string = 0; +float hyphen_rating = MAXFLOAT; +int hyphen_state = 0; + +/*---------------------------------------------------------------------- + F u n c t i o n s +---------------------------------------------------------------------*/ +/********************************************************************** + * set_hyphen_word + * + * If this hyphenated word choice is better than the last one then add + * it as the new word choice. This string can be used on the next + * line to permute the other half of the word. + **********************************************************************/ +void set_hyphen_word(char *word, float rating, int state) { + int char_index = strlen (word) - 1; + + if (display_ratings) + cprintf ("set hyphen word = %s\n", word); + + if (hyphen_rating > rating && char_index > 0) { + word[char_index] = '\0'; + + if (hyphen_string) + strfree(hyphen_string); + hyphen_string = strsave (word); + + hyphen_state = state; + hyphen_rating = rating; + + word[char_index] = '-'; + } +} diff --git a/dict/hyphen.h b/dict/hyphen.h new file mode 100644 index 0000000000..ea978e5858 --- /dev/null +++ b/dict/hyphen.h @@ -0,0 +1,114 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: hyphen.h (Formerly hyphen.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon Jan 14 17:52:50 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef HYPHEN_H +#define HYPHEN_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "choices.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int last_word_on_line; +extern char *hyphen_string; +extern float hyphen_rating; +extern int hyphen_state; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * set_last_word + * + * Set the flag that indicated that this is the last word on a line. + **********************************************************************/ + +#define set_last_word() \ +last_word_on_line = TRUE + +/********************************************************************** + * reset_hyphen_word + * + * Erase the hyphenation word that my have been stored at this location. + **********************************************************************/ + +#define reset_hyphen_word() \ +if (last_word_on_line == FALSE) { \ + if (hyphen_string) strfree (hyphen_string); \ + hyphen_string = NULL; \ + hyphen_rating = MAX_FLOAT32; \ + hyphen_state = 0; \ +} \ + + +/********************************************************************** + * reset_last_word + * + * Reset the flag that indicated that this is the last word on a line. + **********************************************************************/ + +#define reset_last_word() \ +last_word_on_line = FALSE + +/********************************************************************** + * is_last_word + * + * Test the flag that indicated that this is the last word on a line. + **********************************************************************/ + +#define is_last_word() \ +(last_word_on_line) + +/********************************************************************** + * hyphen_base_size + * + * Size of the base word (the part on the line before) of a hyphenated + * coumpound word. + **********************************************************************/ + +#define hyphen_base_size() \ +((! is_last_word () && hyphen_string) ? \ + (strlen (hyphen_string)) : \ + (0)) \ + + +/********************************************************************** + * hyphen_tail + * + * Return the a pointer to the part of the word that was not on the + * previous line. This routine is used for words that were split + * between lines and hyphenated. + **********************************************************************/ + +#define hyphen_tail(word) \ +(& word [hyphen_base_size()]) \ + +/*---------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------*/ +void set_hyphen_word(char *word, float rating, int state); +#endif diff --git a/dict/matchdefs.h b/dict/matchdefs.h new file mode 100644 index 0000000000..0620f0c5f9 --- /dev/null +++ b/dict/matchdefs.h @@ -0,0 +1,143 @@ +/****************************************************************************** + ** Filename: matchdefs.h + ** Purpose: Generic interface definitions for feature matchers. + ** Author: Dan Johnson + ** History: Fri Jan 19 09:21:25 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef MATCHDEFS_H +#define MATCHDEFS_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "general.h" +#include + +/* define the maximum number of classes defined for any matcher + and the maximum class id for any matcher */ +#define MAX_NUM_CLASSES 100 +#define MAX_CLASS_ID 255 + +/* a CLASS_ID is the ascii character to be associated with a class */ +typedef UINT8 CLASS_ID; +#define NO_CLASS 0 + +/* define a type for the index (rather than the class id) of a class. + Class indexes are sequentially defined, while class id's are defined + by the ascii character set. */ +typedef INT16 CLASS_INDEX; +typedef CLASS_INDEX CLASS_TO_INDEX[MAX_CLASS_ID + 1]; +typedef CLASS_ID INDEX_TO_CLASS[MAX_NUM_CLASSES]; +#define ILLEGAL_CLASS (-1) + +/* a PROTO_ID is the index of a prototype within it's class. Valid proto + id's are 0 to N-1 where N is the number of prototypes that make up the + class. */ +typedef INT16 PROTO_ID; +#define NO_PROTO (-1) + +/* FEATURE_ID is the index of a feature within a character description + The feature id ranges from 0 to N-1 where N is the number + of features in a character description. */ +typedef UINT8 FEATURE_ID; +#define NO_FEATURE 255 +#define NOISE_FEATURE 254 +#define MISSING_PROTO 254 +#define MAX_NUM_FEAT 40 +#define MAX_FEATURE_ID 250 + +/* a RATING is the match rating returned by a classifier. + Higher is better. */ +typedef FLOAT32 RATING; + +/* a CERTAINTY is an indication of the degree of confidence of the + classifier. Higher is better. 0 means the match is as good as the + mean of the matches seen in training. -1 means the match was one + standard deviation worse than the training matches, etc. */ +typedef FLOAT32 CERTAINTY; + +/* define a data structure to hold a single match result */ +typedef struct +{ + CLASS_ID Class; + RATING Rating; + CERTAINTY Certainty; +} + + +MATCH_RESULT; + +/* define a data structure for holding an array of match results */ +typedef MATCH_RESULT SORTED_CLASSES[MAX_CLASS_ID + 1]; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +/* all feature matchers that are to be used with the high level + classifier must support the following interface. The names will, of + course, be unique for each different matcher. Note also that + FEATURE_STRUCT is a data structure that is defined specifically for + each feature extractor/matcher pair. + +void InitClassifier (); + +void InitClassifierVars (); + +int TweekClassifier (char *Params); + +void InitQuickGuess (FEATURE_STRUCT *CharFeatures); + +CLASS_ID NextQuickGuess (); + +void MatchCharToClass (CLASS_ID + ClassID, + FEATURE_STRUCT + *CharFeatures, + MATCH_RESULT + *MatchResult); + +void DebugMatch (CLASS_ID + ClassID, + FEATURE_STRUCT + *CharFeatures, + MATCH_RESULT + *MatchResult); + +*/ + +/* misc test functions for proto id's and feature id's */ +#define IsValidFeature(Fid) ((Fid) < MAX_FEATURE_ID) +#define IsValidProto(Pid) ((Pid) >= 0) + +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif + +/* matchdefs.c */ +int CompareMatchResults +_ARGS ((MATCH_RESULT * Result1, MATCH_RESULT * Result2)); + +void PrintMatchResult _ARGS ((FILE * File, MATCH_RESULT * MatchResult)); + +void PrintMatchResults +_ARGS ((FILE * File, int N, MATCH_RESULT MatchResults[])); + +#undef _ARGS + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#endif diff --git a/dict/permdawg.cpp b/dict/permdawg.cpp new file mode 100644 index 0000000000..72f17e7edd --- /dev/null +++ b/dict/permdawg.cpp @@ -0,0 +1,352 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permdawg.c (Formerly permdawg.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 9 15:43:18 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "permdawg.h" +#include "debug.h" +#include "hyphen.h" +#include "permute.h" +#include "tordvars.h" +#include "context.h" +#include "stopper.h" +#include "freelist.h" +#include "globals.h" +#include "dawg.h" +#include + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define FREQ_WERD 1.0 +#define GOOD_WERD 1.1 +#define OK_WERD 1.25 +#define MAX_FREQ_EDGES 1000 +#define NO_RATING -1 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static EDGE_ARRAY frequent_words; +static float rating_margin; +static float rating_pad = 5.0; + +make_toggle_var (dawg_debug, 0, make_dawg_debug, +8, 10, set_dawg_debug, "DAWG Debug "); + +make_float_var (ok_word, OK_WERD, make_ok_word, +8, 17, set_ok_word, "Bad word adjustment"); + +make_float_var (good_word, GOOD_WERD, make_good_word, +8, 18, set_good_word, "Good word adjustment"); + +make_float_var (freq_word, FREQ_WERD, make_freq_word, +8, 19, set_freq_word, "Freq word adjustment"); + +//extern char *demodir; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * adjust_word + * + * Assign an adjusted value to a string that is a word. The value + * that this word choice has is based on case and punctuation rules. + **********************************************************************/ +void adjust_word(A_CHOICE *best_choice, float *certainty_array) { + char *this_word; + int punct_status; + float adjust_factor; + + if (adjust_debug) + cprintf ("%s %4.2f ", + class_string (best_choice), class_probability (best_choice)); + + this_word = class_string (best_choice); + punct_status = punctuation_ok (this_word); + + class_probability (best_choice) += RATING_PAD; + if (case_ok (this_word) && punct_status != -1) { + if (punct_status < 1 && word_in_dawg (frequent_words, this_word)) { + class_probability (best_choice) *= freq_word; + class_permuter (best_choice) = FREQ_DAWG_PERM; + adjust_factor = freq_word; + if (adjust_debug) + cprintf (", F, %4.2f ", freq_word); + } + else { + class_probability (best_choice) *= good_word; + adjust_factor = good_word; + if (adjust_debug) + cprintf (", %4.2f ", good_word); + } + } + else { + class_probability (best_choice) *= ok_word; + adjust_factor = ok_word; + if (adjust_debug) { + if (!case_ok (this_word)) + cprintf (", C"); + if (punctuation_ok (this_word) == -1) + cprintf (", P"); + cprintf (", %4.2f ", ok_word); + } + } + + class_probability (best_choice) -= RATING_PAD; + + LogNewWordChoice(best_choice, adjust_factor, certainty_array); + + if (adjust_debug) + cprintf (" --> %4.2f\n", class_probability (best_choice)); +} + + +/********************************************************************** + * append_next_choice + * + * Check to see whether or not the next choice is worth appending to + * the string being generated. If so then keep going deeper into the + * word. + **********************************************************************/ +void append_next_choice( /*previous option */ + EDGE_ARRAY dawg, + NODE_REF node, + char permuter, + char *word, + CHOICES_LIST choices, + int char_index, + A_CHOICE *this_choice, + char prevchar, + float *limit, + float rating, + float certainty, + float *rating_array, + float *certainty_array, + int word_ending, + int last_word, + CHOICES *result) { + A_CHOICE *better_choice; + /* Add new character */ + word[char_index] = class_string (this_choice)[0]; + word[char_index + 1] = 0; + if (word[char_index] == 0) + word[char_index] = ' '; + certainty_array[char_index] = class_certainty (this_choice); + + rating += class_probability (this_choice); + certainty = min (class_certainty (this_choice), certainty); + + if (rating_array[char_index] == NO_RATING) { + /* Prune bad subwords */ + rating_array[char_index] = rating; + } + else { + if (rating_array[char_index] * rating_margin + rating_pad < rating) { + if (dawg_debug) + cprintf ("early pruned word (%s, rating=%4.2f, limit=%4.2f)\n", + word, rating, *limit); + return; + } + } + + /* Deal with hyphens */ + if (word_ending && last_word && word[char_index] == '-' && char_index > 0) { + *limit = rating; + if (dawg_debug) + cprintf ("new hyphen choice = %s\n", word); + + better_choice = new_choice (word, rating, certainty, -1, permuter); + adjust_word(better_choice, certainty_array); + push_on(*result, better_choice); + set_hyphen_word(word, rating, node); + } + /* Look up char in DAWG */ + else if (letter_is_okay (dawg, &node, char_index, prevchar, + word, word_ending)) { + /* Add a new word choice */ + if (word_ending) { + if (dawg_debug == 1) + cprintf ("new choice = %s\n", word); + *limit = rating; + + better_choice = new_choice (hyphen_tail (word), rating, certainty, + -1, permuter); + adjust_word (better_choice, &certainty_array[hyphen_base_size ()]); + push_on(*result, better_choice); + } + else { + /* Search the next letter */ + JOIN_ON (*result, + dawg_permute (dawg, node, permuter, + choices, char_index + 1, limit, + word, rating, certainty, + rating_array, certainty_array, last_word)); + } + } +} + + +/********************************************************************** + * dawg_permute + * + * Permute all the valid words that can be created with this starting + * point. The node (in the DAWG) and the word string define a base + * from which to start adding the remaining character choices. + **********************************************************************/ +CHOICES dawg_permute(EDGE_ARRAY dawg, + NODE_REF node, + char permuter, + CHOICES_LIST choices, + int char_index, + float *limit, + char *word, + float rating, + float certainty, + float *rating_array, + float *certainty_array, + int last_word) { + CHOICES result = NIL; + CHOICES c; + char *prevchar; + int word_ending = FALSE; + + if (dawg_debug) { + cprintf ("dawg_permute (node=%d, char_index=%d, limit=%4.2f, ", + node, char_index, *limit); + cprintf ("word=%s, rating=%4.2f, certainty=%4.2f)\n", + word, rating, certainty); + } + /* Check for EOW */ + if (1 + char_index == array_count (choices) + hyphen_base_size ()) + word_ending = TRUE; + + if (char_index < array_count (choices) + hyphen_base_size ()) { + prevchar = NULL; + iterate_list (c, + (CHOICES) array_index (choices, + char_index - hyphen_base_size ())) { + append_next_choice (dawg, node, permuter, word, choices, char_index, + (A_CHOICE *) first (c), + prevchar != NULL ? *prevchar : '\0', limit, + rating, certainty, rating_array, certainty_array, + word_ending, last_word, &result); + prevchar = best_string (c); + } + } + + if (result && (dawg_debug == 1)) + print_choices ("dawg_permute", result); + return (result); +} + + +/********************************************************************** + * dawg_permute_and_select + * + * Use a DAWG type data structure to enumerate all the valid strings + * in some gramar. Compare each of the choices against the best choice + * so far. Update the best choice if needed. + **********************************************************************/ +void dawg_permute_and_select(const char *string, + EDGE_ARRAY dawg, + char permuter, + CHOICES_LIST character_choices, + A_CHOICE *best_choice, + INT16 system_words) { + CHOICES result = NIL; + char word[MAX_WERD_LENGTH + 1]; + float certainty_array[MAX_WERD_LENGTH + 1]; + float rating_array[MAX_WERD_LENGTH + 1]; + float rating; + int char_index; + NODE_REF dawg_node = 0; + + /* Pruning margin ratio */ + rating_margin = ok_word / good_word; + + word[0] = '\0'; + rating = class_probability (best_choice); + + for (char_index = 0; char_index < MAX_WERD_LENGTH + 1; char_index++) + rating_array[char_index] = NO_RATING; + char_index = 0; + + if (!is_last_word () && hyphen_string) { + strcpy(word, hyphen_string); + char_index = strlen (hyphen_string); + if (system_words) + dawg_node = hyphen_state; + } + result = dawg_permute (dawg, dawg_node, permuter, character_choices, + char_index, &rating, word, 0.0, 0.0, + rating_array, certainty_array, is_last_word ()); + + if (display_ratings && result) + print_choices(string, result); + + while (result != NIL) { + if (best_probability (result) < class_probability (best_choice)) { + clone_choice (best_choice, first (result)); + } + free_choice (first (result)); + pop_off(result); + } +} + + +/********************************************************************** + * init_permdawg + * + * Initialize the variables needed by this file. + **********************************************************************/ +void init_permdawg() { + char name[1024]; + make_dawg_debug(); + make_ok_word(); + make_good_word(); + make_freq_word(); + + frequent_words = (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * + MAX_FREQ_EDGES); + strcpy(name, demodir); + strcat (name, "tessdata/freq-dawg"); + read_squished_dawg(name, frequent_words, MAX_FREQ_EDGES); +} + +void end_permdawg() { + memfree(frequent_words); + frequent_words = NULL; +} + +/********************************************************************** + * test_freq_words() + * + * Tests a word against the frequent word dawg + **********************************************************************/ +int test_freq_words(const char *word) { + return (word_in_dawg (frequent_words, word)); +} diff --git a/dict/permdawg.h b/dict/permdawg.h new file mode 100644 index 0000000000..0f41732107 --- /dev/null +++ b/dict/permdawg.h @@ -0,0 +1,94 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permdawg.h (Formerly permdawg.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon May 20 16:45:29 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef PERMDAWG_H +#define PERMDAWG_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "dawg.h" +#include "choices.h" +#include "choicearr.h" + +/*--------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int dawg_debug; +extern float ok_word; +extern float good_word; +extern float freq_word; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------*/ +void adjust_word(A_CHOICE *best_choice, float *certainty_array); + + /*previous option */ +void append_next_choice(EDGE_ARRAY dawg, + NODE_REF node, + char permuter, + char *word, + CHOICES_LIST choices, + int char_index, + A_CHOICE *this_choice, + char prevchar, + float *limit, + float rating, + float certainty, + float *rating_array, + float *certainty_array, + int word_ending, + int last_word, + CHOICES *result); + +CHOICES dawg_permute(EDGE_ARRAY dawg, + NODE_REF node, + char permuter, + CHOICES_LIST choices, + int char_index, + float *limit, + char *word, + float rating, + float certainty, + float *rating_array, + float *certainty_array, + int last_word); + +void dawg_permute_and_select(const char *string, + EDGE_ARRAY dawg, + char permuter, + CHOICES_LIST character_choices, + A_CHOICE *best_choice, + INT16 system_words); + +void init_permdawg(); +void end_permdawg(); + +int test_freq_words(const char *word); +#endif diff --git a/dict/permnum.cpp b/dict/permnum.cpp new file mode 100644 index 0000000000..2c1afbc317 --- /dev/null +++ b/dict/permnum.cpp @@ -0,0 +1,483 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permnum.c (Formerly permnum.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 2 14:12:43 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "const.h" +#include "permnum.h" +#include "debug.h" +#include "permute.h" +#include "dawg.h" +#include "tordvars.h" +#include "stopper.h" + +#include +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static const char *allowed_alpha_strs[] = { + "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec", NULL +}; + +static const char *allowed_char_strs[] = { + "adfjmnos", "aceopu", "bcglnrptvy" +}; + +const int kNumStates = 7; + +static int number_state_table[kNumStates][8] = { { + /* 0. Beginning of string */ + /* l d o a t 1 2 3 */ + 0, 1, 1, -99, -99, 4, -99, -99 + }, + { /* 1. After a digit or operator */ + -99, 1, 1, 3, 2, 4, 3, 3 + }, + { /* 2. After trailing punctuation */ + -99, -99, 1, -99, 2, -99, -99, -99 + }, + { /* 3. After a alpha character */ + -99, -99, 3, 3, 2, 3, 3, 3 + }, + { /* 4. After 1st char */ + -99, -1, -1, -99, -2, -99, 5, -99 + }, + { /* 5. After 2nd char */ + -99, -1, -1, -99, -2, -99, -99, 6 + }, + { /* 6. After 3rd char */ + -99, -1, -1, -99, -2, -99, -99, -99 + } +}; + +// The state is coded with its true state shifted left by kStateShift. +// A repeat count (starting with 0) is stored in the lower bits +// No state is allowed to occur more than kMaxRepeats times. +const int kStateShift = 4; +const int kRepeatMask = (1 << kStateShift) - 1; + +const int kMaxRepeats[kNumStates] = { + 3, 10, 3, 3, 3, 3, 3 +}; + +make_float_var (good_number, GOOD_NUMBER, make_good_number, +8, 15, set_good_number, "Good number adjustment"); + +make_float_var (ok_number, OK_NUMBER, make_ok_number, +8, 16, set_ok_number, "Bad number adjustment"); + +make_toggle_var (number_debug, 0, make_number_debug, +8, 23, set_number_debug, "Number debug"); + +make_int_var (number_depth, 3, make_number_depth, +8, 24, set_number_depth, "Number depth"); + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * isleading + * + * Return non-zero if this is a leading type punctuation mark for the + * numeric grammar. + **********************************************************************/ + +#define isleading(ch) \ +((ch == '{' ) || \ + (ch == '[' ) || \ + (ch == '(' ) || \ + (ch == '#' ) || \ + (ch == '@' ) || \ + (ch == '$' )) + +/********************************************************************** + * istrailing + * + * Return non-zero if this is a leading type punctuation mark for the + * numeric grammar. + **********************************************************************/ + +#define istrailing(ch) \ +((ch == '}' ) || \ + (ch == ']' ) || \ + (ch == ')' ) || \ + (ch == ';' ) || \ + (ch == ':' ) || \ + (ch == ',' ) || \ + (ch == '.' ) || \ + (ch == '%' )) + +/********************************************************************** + * isoperator + * + * Return non-zero if this is a leading type punctuation mark for the + * numeric grammar. + **********************************************************************/ + +#define isoperator(ch) \ +((ch == '*' ) || \ + (ch == '+' ) || \ + (ch == '-' ) || \ + (ch == '/' ) || \ + (ch == '.' ) || \ + (ch == ':' ) || \ + (ch == ',' )) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * adjust_number + * + * Assign an adjusted value to a string that is a word. The value + * that this word choice has is based on case and punctuation rules. + **********************************************************************/ +void adjust_number(A_CHOICE *best_choice, float *certainty_array) { + float adjust_factor; + + if (adjust_debug) + cprintf ("Number: %s %4.2f ", + class_string (best_choice), class_probability (best_choice)); + + class_probability (best_choice) += RATING_PAD; + if (pure_number (class_string (best_choice))) { + class_probability (best_choice) *= good_number; + adjust_factor = good_number; + if (adjust_debug) + cprintf (", %4.2f ", good_number); + } + else { + class_probability (best_choice) *= ok_number; + adjust_factor = ok_number; + if (adjust_debug) + cprintf (", N, %4.2f ", ok_number); + } + + class_probability (best_choice) -= RATING_PAD; + LogNewWordChoice(best_choice, adjust_factor, certainty_array); + if (adjust_debug) + cprintf (" --> %4.2f\n", class_probability (best_choice)); +} + + +/********************************************************************** + * append_number_choices + * + * Check to see whether or not the next choice is worth appending to + * the string being generated. If so then keep going deeper into the + * word. + **********************************************************************/ +void append_number_choices(int state, + char *word, + CHOICES_LIST choices, + int char_index, + A_CHOICE *this_choice, + float *limit, + float rating, + float certainty, + float *certainty_array, + CHOICES *result) { + int word_ending = FALSE; + int x; + + if (char_index == (array_count (choices) - 1)) + word_ending = TRUE; + + word[char_index] = class_string (this_choice)[0]; + word[char_index + 1] = '\0'; + if (word[char_index] == '\0') + word[char_index] = ' '; + certainty_array[char_index] = class_certainty (this_choice); + + rating += class_probability (this_choice); + certainty = min (class_certainty (this_choice), certainty); + + if (rating < *limit) { + + state = number_state_change (state, word + char_index); + if (number_debug) + cprintf ("%-20s prob=%4.2f state=%d\n", word, rating, state); + + if (state != -1) { + + if ((state >> kStateShift) == 3 && + char_index + 3 < array_count (choices)) { + return; + } + + if (word_ending) { + for (x = 0; x <= char_index; x++) { + if (isdigit (word[x])) { + if (number_debug) + cprintf ("new choice = %s\n", word); + push_on (*result, new_choice (word, rating, certainty, + -1, NUMBER_PERM)); + adjust_number ((A_CHOICE *) first (*result), + certainty_array); + if (best_probability (*result) > *limit) { + free_choice (first (*result)); + pop_off(*result); + } + else { + *limit = best_probability (*result); + break; + } + } + } + } + else { + JOIN_ON (*result, + number_permute (state, choices, char_index + 1, limit, + word, rating, certainty, + certainty_array)); + } + } + } + else { + if (number_debug) + cprintf ("pruned word (%s, rating=%4.2f, limit=%4.2f)\n", + word, rating, *limit); + } +} + + +/********************************************************************** + * init_permute + * + * Initialize anything that needs to be set up for the permute + * functions. + **********************************************************************/ +void init_permnum() { + make_good_number(); + make_ok_number(); + make_number_debug(); + make_number_depth(); +} + + +/********************************************************************** + * number_character_type + * + * Decide which type of a character (with regard to the numeric state + * table) we are looking at. + **********************************************************************/ +int number_character_type( //current state + char ch, + int state) { + char lower_char = tolower (ch); + + if (isalpha (ch)) { + if (state < 4 && strchr (allowed_char_strs[0], lower_char) != NULL) + return 5; + else if (state == 4 + && strchr (allowed_char_strs[1], lower_char) != NULL) + return 6; + else if (state == 5 + && strchr (allowed_char_strs[2], lower_char) != NULL) + return 7; + return 3; + } + else if (isdigit (ch)) + return (1); + else if (isoperator (ch)) + return (2); + else if (istrailing (ch)) + return (4); + else if (isleading (ch)) + return (0); + else + return (-1); +} + + +/********************************************************************** + * number_state_change + * + * Execute a state transition according to the state table and + * additional rules. + **********************************************************************/ +int number_state_change(int state, //current state + const char *word) { //current char + int char_type; //type of char + int new_state; //state to return + int old_state = state >> kStateShift; + int repeats = state & kRepeatMask; + int index; + char copy_word[4]; //tolowered chars + + char_type = number_character_type (*word, old_state); + if (char_type == -1) + return -1; + new_state = number_state_table[old_state][char_type]; + if (new_state == old_state) { + ++repeats; + if (repeats >= kMaxRepeats[old_state]) + return -1; + } else { + repeats = 0; + } + if (new_state >= 0) + return (new_state << kStateShift) | repeats; + if (new_state == -99) + return -1; + + //now check to see if the last state-3 chars in the word + //make an allowable word. For now only 3 letter words + //are allowed + if (old_state != 6) + return -1; //only 3 letters now + copy_word[0] = tolower (word[-3]); + copy_word[1] = tolower (word[-2]); + copy_word[2] = tolower (word[-1]); + copy_word[3] = '\0'; + for (index = 0; allowed_alpha_strs[index] != NULL; index++) { + if (strcmp (copy_word, allowed_alpha_strs[index]) == 0) + return (-new_state) << kStateShift; + } + return -1; //not a good word +} + + +/********************************************************************** + * number_permute + * + * Permute all the valid string that match the 'grammar' of numbers. + * The valid syntax for numbers is encoded in a state table. The + * permuter uses this state table to enumerate all the string that + * can be produced using the input choices. + **********************************************************************/ +CHOICES number_permute(int state, + CHOICES_LIST choices, + int char_index, + float *limit, + char *word, + float rating, + float certainty, + float *certainty_array) { + CHOICES result = NIL; + CHOICES c; + int depth = 0; + + if (number_debug) { + cprintf ("number_permute (state=%d, char_index=%d, limit=%4.2f, ", + state, char_index, *limit); + cprintf ("word=%s, rating=%4.2f, certainty=%4.2f)\n", + word, rating, certainty); + } + if (char_index < array_count (choices)) { + iterate_list (c, (CHOICES) array_index (choices, char_index)) { + if (depth++ < number_depth) + append_number_choices (state, word, choices, char_index, + (A_CHOICE *) first (c), limit, rating, + certainty, certainty_array, &result); + } + } + if (result && number_debug == 1) + print_choices ("number_permute:", result); + return (result); +} + + +/********************************************************************** + * number_permute_and_select + * + * Permute all the possible valid numbers and adjust their ratings. + * Save the best rating. + **********************************************************************/ +A_CHOICE *number_permute_and_select(CHOICES_LIST char_choices, + float rating_limit) { + CHOICES result = NIL; + char word[MAX_WERD_LENGTH + 1]; + float certainty_array[MAX_WERD_LENGTH + 1]; + float rating = rating_limit; + A_CHOICE *best_choice; + + best_choice = new_choice (NULL, MAXFLOAT, -MAXFLOAT, -1, NO_PERM); + + if (array_count (char_choices) <= MAX_WERD_LENGTH) { + word[0] = '\0'; + result = number_permute (0, char_choices, 0, &rating, + word, 0.0, 0.0, certainty_array); + + if (display_ratings && result) + print_choices ("number_permuter", result); + + while (result != NIL) { + if (best_probability (result) < class_probability (best_choice)) { + clone_choice (best_choice, first (result)); + } + free_choice (first (result)); + pop_off(result); + } + } + return (best_choice); +} + + +/********************************************************************** + * pure_number + * + * Check to see if this string is a pure number (one that does not end + * with alphabetic characters). + **********************************************************************/ +int pure_number(const char *string) { + int x; + + for (x = strlen (string) - 1; x >= 0; x--) { + if (isdigit (string[x])) { + return (TRUE); + } + else if (isalpha (string[x])) + return (FALSE); + } + return (FALSE); +} + + +/********************************************************************** + * valid_number + * + * Check this string to see if it is a valid number. Return TRUE if + * it is. + **********************************************************************/ +int valid_number(const char *string) { + int state = 0; + int char_index; + int num_chars = strlen (string); + int num_digits = 0; + + for (char_index = 0; char_index < num_chars; char_index++) { + + state = number_state_change (state, string + char_index); + if (state == -1) + return (FALSE); + if (isdigit (string[char_index])) + num_digits++; + } + return num_digits > num_chars - num_digits; +} diff --git a/dict/permnum.h b/dict/permnum.h new file mode 100644 index 0000000000..0b097173fe --- /dev/null +++ b/dict/permnum.h @@ -0,0 +1,79 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permnum.h (Formerly permnum.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon May 20 16:30:03 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef PERMNUM_H +#define PERMNUM_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "choicearr.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +#define GOOD_NUMBER 1.1 +#define OK_NUMBER 1.4 +extern float ok_number; +extern float good_number; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void adjust_number(A_CHOICE *best_choice, float *certainty_array); + +void append_number_choices(int state, + char *word, + CHOICES_LIST choices, + int char_index, + A_CHOICE *this_choice, + float *limit, + float rating, + float certainty, + float *certainty_array, + CHOICES *result); + +void init_permnum(); + +int number_character_type(char ch, int state); + + //current state +int number_state_change(int state, const char *word); + +CHOICES number_permute(int state, + CHOICES_LIST choices, + int char_index, + float *limit, + char *word, + float rating, + float certainty, + float *certainty_array); + +A_CHOICE *number_permute_and_select(CHOICES_LIST char_choices, + float rating_limit); + +int pure_number(const char *string); + +int valid_number(const char *string); +#endif diff --git a/dict/permute.cpp b/dict/permute.cpp new file mode 100644 index 0000000000..75a56a9ba2 --- /dev/null +++ b/dict/permute.cpp @@ -0,0 +1,1587 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permute.c (Formerly permute.c) + * Description: Handle the new ratings choices for Wise Owl + * Author: Mark Seaman, OCR Technology + * Created: Fri Sep 22 14:05:51 1989 + * Modified: Thu Jan 3 16:38:46 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------*/ +#include "permute.h" +#include "globals.h" +#include "permdawg.h" +#include "debug.h" +#include "tordvars.h" +#include "hyphen.h" +#include "stopper.h" +#include "trie.h" +#include "context.h" +#include "permnum.h" +#include "freelist.h" +#include "callcpp.h" + +#include + +int permutation_count; // Used in metrics.cpp. +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +#define MAX_NUM_EDGES 60000 +#define MAX_DOC_EDGES 250000 +#define RESERVED_DOC_EDGES 10000 +#define MAX_USER_EDGES 20000 +#define USER_RESERVED_EDGES 2000 + /* Weights for adjustment */ +#define NON_WERD 1.25 +#define GARBAGE_STRING 1.5 +#define MAX_PERM_LENGTH 128 + +static EDGE_ARRAY pending_words; +static EDGE_ARRAY document_words; +static EDGE_ARRAY user_words; +static EDGE_ARRAY word_dawg; + +make_toggle_var (adjust_debug, 0, make_adjust_debug, +8, 13, set_adjust_debug, "Adjustment Debug"); + +make_toggle_var (compound_debug, 0, make_compound_debug, +8, 14, set_compound_debug, "Compound Debug"); + +make_float_var (non_word, NON_WERD, make_non_word, +8, 20, set_non_word, "Non-word adjustment"); + +make_float_var (garbage, GARBAGE_STRING, make_garbage, +8, 21, set_garbage, "Garbage adjustment"); + +make_toggle_var (save_doc_words, 0, make_doc_words, +8, 22, set_doc_words, "Save Document Words "); + +make_toggle_var (doc_dict_enable, 1, make_doc_dict, +8, 25, set_doc_dict, "Enable Document Dictionary "); +/* PREV DEFAULT 0 */ + +int permute_only_top = 0; + + //0x0=. +static INT32 bigram_counts[256][3] = { { + 0, 0, 0 + }, + { //0x1=. + 0, 0, 0 + }, + { //0x2=. + 0, 0, 0 + }, + { //0x3=. + 0, 0, 0 + }, + { //0x4=. + 0, 0, 0 + }, + { //0x5=. + 0, 0, 0 + }, + { //0x6=. + 0, 0, 0 + }, + { //0x7=. + 0, 0, 0 + }, + { //0x8=. + 0, 0, 0 + }, + { //0x9=. + 0, 0, 0 + }, + { //0xa=. + 93, 28, 0 + }, + { //0xb=. + 0, 0, 0 + }, + { //0xc=. + 0, 0, 0 + }, + { //0xd=. + 0, 0, 0 + }, + { //0xe=. + 0, 0, 0 + }, + { //0xf=. + 0, 0, 0 + }, + { //0x10=. + 0, 0, 0 + }, + { //0x11=. + 0, 0, 0 + }, + { //0x12=. + 0, 0, 0 + }, + { //0x13=. + 0, 0, 0 + }, + { //0x14=. + 0, 0, 0 + }, + { //0x15=. + 0, 0, 0 + }, + { //0x16=. + 0, 0, 0 + }, + { //0x17=. + 0, 0, 0 + }, + { //0x18=. + 0, 0, 0 + }, + { //0x19=. + 0, 0, 0 + }, + { //0x1a=. + 0, 0, 0 + }, + { //0x1b=. + 0, 0, 0 + }, + { //0x1c=. + 0, 0, 0 + }, + { //0x1d=. + 0, 0, 0 + }, + { //0x1e=. + 0, 0, 0 + }, + { //0x1f=. + 0, 0, 0 + }, + { //0x20= + 324, 377, 2 + }, + { //0x21=! + 2, 1, 0 + }, + { //0x22=" + 2, 1, 0 + }, + { //0x23=# + 1, 0, 1 + }, + { //0x24=$ + 2, 1, 0 + }, + { //0x25=% + 2, 0, 0 + }, + { //0x26=& + 2, 1, 0 + }, + { //0x27=' + 1, 21, 8 + }, + { //0x28=( + 2, 1, 0 + }, + { //0x29=) + 19, 0, 0 + }, + { //0x2a=* + 2, 1, 0 + }, + { //0x2b=+ + 1, 0, 0 + }, + { //0x2c=, + 75, 4, 0 + }, + { //0x2d=- + 52, 7, 0 + }, + { //0x2e=. + 190, 16, 3 + }, + { //0x2f=/ + 53, 2, 0 + }, + { //0x30=0 + 399, 0, 0 + }, + { //0x31=1 + 220, 0, 0 + }, + { //0x32=2 + 226, 0, 0 + }, + { //0x33=3 + 128, 0, 0 + }, + { //0x34=4 + 147, 0, 0 + }, + { //0x35=5 + 179, 0, 1 + }, + { //0x36=6 + 173, 0, 0 + }, + { //0x37=7 + 115, 0, 0 + }, + { //0x38=8 + 107, 0, 0 + }, + { //0x39=9 + 934, 0, 1 + }, + { //0x3a=: + 27, 0, 1 + }, + { //0x3b=; + 2, 1, 0 + }, + { //0x3c=< + 2, 1, 0 + }, + { //0x3d== + 2, 1, 0 + }, + { //0x3e=> + 2, 1, 0 + }, + { //0x3f=? + 2, 1, 0 + }, + { //0x40=@ + 2, 1, 0 + }, + { //0x41=A + 3, 1, 0 + }, + { //0x42=B + 1, 73, 0 + }, + { //0x43=C + 1, 6, 0 + }, + { //0x44=D + 1, 24, 0 + }, + { //0x45=E + 1, 2, 0 + }, + { //0x46=F + 1, 19, 0 + }, + { //0x47=G + 1, 2, 0 + }, + { //0x48=H + 3, 2, 1 + }, + { //0x49=I + 0, 68, 0 + }, + { //0x4a=J + 1, 2, 0 + }, + { //0x4b=K + 1, 2, 0 + }, + { //0x4c=L + 1, 82, 0 + }, + { //0x4d=M + 10, 10, 0 + }, + { //0x4e=N + 3, 239, 0 + }, + { //0x4f=O + 1, 10, 0 + }, + { //0x50=P + 0, 1, 3 + }, + { //0x51=Q + 2, 3, 0 + }, + { //0x52=R + 1, 43, 0 + }, + { //0x53=S + 1, 53, 0 + }, + { //0x54=T + 2, 18, 0 + }, + { //0x55=U + 1, 2, 0 + }, + { //0x56=V + 1, 17, 0 + }, + { //0x57=W + 1, 5, 0 + }, + { //0x58=X + 1, 6, 0 + }, + { //0x59=Y + 1, 2, 0 + }, + { //0x5a=Z + 1, 2, 0 + }, + { //0x5b=[ + 2, 1, 0 + }, + { //0x5c=backslash + 2, 1, 0 + }, + { //0x5d=] + 2, 1, 0 + }, + { //0x5e=^ + 2, 1, 0 + }, + { //0x5f=_ + 2, 1, 0 + }, + { //0x60=` + 1, 0, 2 + }, + { //0x61=a + 0, 0, 671 + }, + { //0x62=b + 0, 1, 16 + }, + { //0x63=c + 0, 2, 1 + }, + { //0x64=d + 0, 14, 0 + }, + { //0x65=e + 0, 0, 763 + }, + { //0x66=f + 0, 186, 0 + }, + { //0x67=g + 0, 2, 1 + }, + { //0x68=h + 0, 2, 1 + }, + { //0x69=i + 0, 0, 818 + }, + { //0x6a=j + 0, 2, 1 + }, + { //0x6b=k + 0, 4, 1 + }, + { //0x6c=l + 0, 26, 3 + }, + { //0x6d=m + 0, 69, 0 + }, + { //0x6e=n + 0, 885, 0 + }, + { //0x6f=o + 0, 17, 722 + }, + { //0x70=p + 0, 1, 5 + }, + { //0x71=q + 2, 1, 0 + }, + { //0x72=r + 0, 21, 0 + }, + { //0x73=s + 3, 49, 0 + }, + { //0x74=t + 0, 219, 5 + }, + { //0x75=u + 0, 0, 56 + }, + { //0x76=v + 0, 4, 0 + }, + { //0x77=w + 0, 2, 1 + }, + { //0x78=x + 0, 2, 1 + }, + { //0x79=y + 0, 1, 23 + }, + { //0x7a=z + 0, 2, 1 + }, + { //0x7b={ + 2, 1, 0 + }, + { //0x7c=| + 59, 0, 3 + }, + { //0x7d=} + 2, 1, 0 + }, + { //0x7e=~ + 2, 1, 0 + }, + { //0x7f=. + 0, 0, 0 + }, + { //0x80=. + 0, 0, 0 + }, + { //0x81=. + 0, 0, 0 + }, + { //0x82=. + 0, 0, 0 + }, + { //0x83=. + 0, 0, 0 + }, + { //0x84=. + 0, 0, 0 + }, + { //0x85=. + 0, 0, 0 + }, + { //0x86=. + 0, 0, 0 + }, + { //0x87=. + 0, 0, 0 + }, + { //0x88=. + 0, 0, 0 + }, + { //0x89=. + 0, 0, 0 + }, + { //0x8a=. + 0, 0, 0 + }, + { //0x8b=. + 0, 0, 0 + }, + { //0x8c=. + 0, 0, 0 + }, + { //0x8d=. + 0, 0, 0 + }, + { //0x8e=. + 0, 0, 0 + }, + { //0x8f=. + 0, 0, 0 + }, + { //0x90=. + 0, 0, 0 + }, + { //0x91=. + 0, 0, 0 + }, + { //0x92=. + 0, 0, 0 + }, + { //0x93=. + 0, 0, 0 + }, + { //0x94=. + 0, 0, 0 + }, + { //0x95=. + 0, 0, 0 + }, + { //0x96=. + 0, 0, 0 + }, + { //0x97=. + 0, 0, 0 + }, + { //0x98=. + 0, 0, 0 + }, + { //0x99=. + 0, 0, 0 + }, + { //0x9a=. + 0, 0, 0 + }, + { //0x9b=. + 0, 0, 0 + }, + { //0x9c=. + 0, 0, 0 + }, + { //0x9d=. + 0, 0, 0 + }, + { //0x9e=. + 0, 0, 0 + }, + { //0x9f=. + 0, 0, 0 + }, + { //0xa0=. + 0, 0, 0 + }, + { //0xa1=. + 0, 0, 0 + }, + { //0xa2=. + 0, 0, 0 + }, + { //0xa3=. + 0, 0, 0 + }, + { //0xa4=. + 0, 0, 0 + }, + { //0xa5=. + 0, 0, 0 + }, + { //0xa6=. + 0, 0, 0 + }, + { //0xa7=. + 0, 0, 0 + }, + { //0xa8=. + 0, 0, 0 + }, + { //0xa9=. + 0, 0, 0 + }, + { //0xaa=. + 0, 0, 0 + }, + { //0xab=. + 0, 0, 0 + }, + { //0xac=. + 0, 0, 0 + }, + { //0xad=. + 0, 0, 0 + }, + { //0xae=. + 0, 0, 0 + }, + { //0xaf=. + 0, 0, 0 + }, + { //0xb0=. + 0, 0, 0 + }, + { //0xb1=. + 0, 0, 0 + }, + { //0xb2=. + 0, 0, 0 + }, + { //0xb3=. + 0, 0, 0 + }, + { //0xb4=. + 0, 0, 0 + }, + { //0xb5=. + 0, 0, 0 + }, + { //0xb6=. + 0, 0, 0 + }, + { //0xb7=. + 0, 0, 0 + }, + { //0xb8=. + 0, 0, 0 + }, + { //0xb9=. + 0, 0, 0 + }, + { //0xba=. + 0, 0, 0 + }, + { //0xbb=. + 0, 0, 0 + }, + { //0xbc=. + 0, 0, 0 + }, + { //0xbd=. + 0, 0, 0 + }, + { //0xbe=. + 0, 0, 0 + }, + { //0xbf=. + 0, 0, 0 + }, + { //0xc0=. + 0, 0, 0 + }, + { //0xc1=. + 0, 0, 0 + }, + { //0xc2=. + 0, 0, 0 + }, + { //0xc3=. + 0, 0, 0 + }, + { //0xc4=. + 0, 0, 0 + }, + { //0xc5=. + 0, 0, 0 + }, + { //0xc6=. + 0, 0, 0 + }, + { //0xc7=. + 0, 0, 0 + }, + { //0xc8=. + 0, 0, 0 + }, + { //0xc9=. + 0, 0, 0 + }, + { //0xca=. + 0, 0, 0 + }, + { //0xcb=. + 0, 0, 0 + }, + { //0xcc=. + 0, 0, 0 + }, + { //0xcd=. + 0, 0, 0 + }, + { //0xce=. + 0, 0, 0 + }, + { //0xcf=. + 0, 0, 0 + }, + { //0xd0=. + 0, 0, 0 + }, + { //0xd1=. + 0, 0, 0 + }, + { //0xd2=. + 0, 0, 0 + }, + { //0xd3=. + 0, 0, 0 + }, + { //0xd4=. + 0, 0, 0 + }, + { //0xd5=. + 0, 0, 0 + }, + { //0xd6=. + 0, 0, 0 + }, + { //0xd7=. + 0, 0, 0 + }, + { //0xd8=. + 0, 0, 0 + }, + { //0xd9=. + 0, 0, 0 + }, + { //0xda=. + 0, 0, 0 + }, + { //0xdb=. + 0, 0, 0 + }, + { //0xdc=. + 0, 0, 0 + }, + { //0xdd=. + 0, 0, 0 + }, + { //0xde=. + 0, 0, 0 + }, + { //0xdf=. + 0, 0, 0 + }, + { //0xe0=. + 0, 0, 0 + }, + { //0xe1=. + 0, 0, 0 + }, + { //0xe2=. + 0, 0, 0 + }, + { //0xe3=. + 0, 0, 0 + }, + { //0xe4=. + 0, 0, 0 + }, + { //0xe5=. + 0, 0, 0 + }, + { //0xe6=. + 0, 0, 0 + }, + { //0xe7=. + 0, 0, 0 + }, + { //0xe8=. + 0, 0, 0 + }, + { //0xe9=. + 0, 0, 0 + }, + { //0xea=. + 0, 0, 0 + }, + { //0xeb=. + 0, 0, 0 + }, + { //0xec=. + 0, 0, 0 + }, + { //0xed=. + 0, 0, 0 + }, + { //0xee=. + 0, 0, 0 + }, + { //0xef=. + 0, 0, 0 + }, + { //0xf0=. + 0, 0, 0 + }, + { //0xf1=. + 0, 0, 0 + }, + { //0xf2=. + 0, 0, 0 + }, + { //0xf3=. + 0, 0, 0 + }, + { //0xf4=. + 0, 0, 0 + }, + { //0xf5=. + 0, 0, 0 + }, + { //0xf6=. + 0, 0, 0 + }, + { //0xf7=. + 0, 0, 0 + }, + { //0xf8=. + 0, 0, 0 + }, + { //0xf9=. + 0, 0, 0 + }, + { //0xfa=. + 0, 0, 0 + }, + { //0xfb=. + 0, 0, 0 + }, + { //0xfc=. + 0, 0, 0 + }, + { //0xfd=. + 0, 0, 0 + }, + { //0xfe=. + 0, 0, 0 + }, + { //0xff=. + 0, 0, 0 + }, +}; + +//extern "C" double permuter_pending_threshold; + + /* Similarity matcher values */ +#define SIM_CERTAINTY_SCALE -10.0 + /* Similarity matcher values */ +#define SIM_CERTAINTY_OFFSET -10.0 + /* Worst E*L product to stop on */ +#define SIMILARITY_FLOOR 100.0 +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ + +/********************************************************************** + * good_choice + * + * Return TRUE if a good answer is found for the unknown blob rating. + **********************************************************************/ +int good_choice(A_CHOICE *choice) { + register float certainty; + if (choice == NULL) + return (FALSE); + if (similarity_enable) { + if ((class_probability (choice) + 1) * class_certainty (choice) > + SIMILARITY_FLOOR) + return (FALSE); + certainty = + SIM_CERTAINTY_OFFSET + + class_probability (choice) * SIM_CERTAINTY_SCALE; + } + + else { + certainty = class_certainty (choice); + } + if (certainty > certainty_threshold) { + return (TRUE); + } + + else { + return (FALSE); + } +} + + +/********************************************************************** + * add_document_word + * + * Add a word found on this document to the document specific + * dictionary. + **********************************************************************/ +void add_document_word(A_CHOICE *best_choice) { + char filename[CHARS_PER_LINE]; + FILE *doc_word_file; + char *string; + int stringlen; //length of word + + string = class_string (best_choice); + stringlen = strlen (string); + + if (!doc_dict_enable + || valid_word (string) || CurrentWordAmbig () || stringlen < 2) + return; + + if (!good_choice (best_choice) || stringlen == 2) { + if (class_certainty (best_choice) < permuter_pending_threshold) + return; + if (!word_in_dawg (pending_words, string)) { + if (stringlen > 2 || isupper (string[0]) && isupper (string[1])) + add_word_to_dawg(pending_words, + string, + MAX_DOC_EDGES, + RESERVED_DOC_EDGES); + return; + } + } + + if (save_doc_words) { + strcpy(filename, imagefile); + strcat (filename, ".doc"); + doc_word_file = open_file (filename, "a"); + fprintf (doc_word_file, "%s\n", string); + fclose(doc_word_file); + } + add_word_to_dawg(document_words, string, MAX_DOC_EDGES, RESERVED_DOC_EDGES); + case_sensative = FALSE; +} + + +/********************************************************************** + * adjust_non_word + * + * Assign an adjusted value to a string that is a non-word. The value + * that this word choice has is based on case and punctuation rules. + **********************************************************************/ +void +adjust_non_word (A_CHOICE * best_choice, float certainties[]) { + char *this_word; + float adjust_factor; + + if (adjust_debug) + cprintf ("%s %4.2f ", + class_string (best_choice), class_probability (best_choice)); + + this_word = class_string (best_choice); + + class_probability (best_choice) += RATING_PAD; + if (case_ok (this_word) && punctuation_ok (this_word) != -1) { + class_probability (best_choice) *= non_word; + adjust_factor = non_word; + if (adjust_debug) + cprintf (", %4.2f ", non_word); + } + else { + class_probability (best_choice) *= garbage; + adjust_factor = garbage; + if (adjust_debug) { + if (!case_ok (this_word)) + cprintf (", C"); + if (punctuation_ok (this_word) == -1) + cprintf (", P"); + cprintf (", %4.2f ", garbage); + } + } + + class_probability (best_choice) -= RATING_PAD; + + LogNewWordChoice(best_choice, adjust_factor, certainties); + + if (adjust_debug) + cprintf (" --> %4.2f\n", class_probability (best_choice)); +} + + +/********************************************************************** + * init_permute + * + * Initialize anything that needs to be set up for the permute + * functions. + **********************************************************************/ +void init_permute() { + char name[1024]; + make_adjust_debug(); + make_compound_debug(); + make_non_word(); + make_garbage(); + make_doc_words(); + make_doc_dict(); + + init_permdawg(); + init_permnum(); + + word_dawg = (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_NUM_EDGES); + strcpy(name, demodir); + strcat (name, "tessdata/word-dawg"); + read_squished_dawg(name, word_dawg, MAX_NUM_EDGES); + + document_words = + (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_DOC_EDGES); + initialize_dawg(document_words, MAX_DOC_EDGES); + + pending_words = + (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_DOC_EDGES); + initialize_dawg(pending_words, MAX_DOC_EDGES); + + user_words = (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_USER_EDGES); + strcpy(name, demodir); + strcat (name, "tessdata/user-words"); + read_word_list(name, user_words, MAX_USER_EDGES, USER_RESERVED_EDGES); + case_sensative = FALSE; +} + +void end_permute() { + memfree(word_dawg); + word_dawg = NULL; + memfree(document_words); + document_words = NULL; + memfree(pending_words); + pending_words = NULL; + memfree(user_words); + user_words = NULL; +} + +/********************************************************************** + * permute_all + * + * Permute all the characters together using all of the different types + * of permuters/selectors available. Each of the characters must have + * a non-NIL choice list. + **********************************************************************/ +A_CHOICE *permute_all(CHOICES_LIST char_choices, + float rating_limit, + A_CHOICE *raw_choice) { + A_CHOICE *result_1; + A_CHOICE *result_2 = NULL; + BOOL8 any_alpha; + + result_1 = permute_top_choice (char_choices, rating_limit, raw_choice, + &any_alpha); + if (result_1 == NULL) + return (NULL); + if (permute_only_top) + return result_1; + if (any_alpha && array_count (char_choices) <= 20) { + result_2 = permute_words (char_choices, rating_limit); + + if (class_probability (result_1) < class_probability (result_2) + || class_string (result_2) == NULL) { + free_choice(result_2); + } + else { + free_choice(result_1); + result_1 = result_2; + } + } + + result_2 = number_permute_and_select (char_choices, rating_limit); + + if (class_probability (result_1) < class_probability (result_2) + || class_string (result_2) == NULL) { + free_choice(result_2); + } + else { + free_choice(result_1); + result_1 = result_2; + } + + result_2 = permute_compound_words (char_choices, rating_limit); + + if (!result_2 || + class_probability (result_1) < class_probability (result_2) + || class_string (result_2) == NULL) { + free_choice(result_2); + } + else { + free_choice(result_1); + result_1 = result_2; + } + + return (result_1); +} + + +/********************************************************************** + * permute_characters + * + * Permute these characters together according to each of the different + * permuters that are enabled. + **********************************************************************/ +void permute_characters(CHOICES_LIST char_choices, + float limit, + A_CHOICE *best_choice, + A_CHOICE *raw_choice) { + A_CHOICE *this_choice; + + permutation_count++; /* Global counter */ + + this_choice = permute_all (char_choices, limit, raw_choice); + + if (this_choice && + class_probability (this_choice) < class_probability (best_choice)) { + clone_choice(best_choice, this_choice); + } + free_choice(this_choice); + + if (display_ratings) + cprintf ("permute_characters: %-15s %4.2f %4.2f\n", + class_string (best_choice), + class_probability (best_choice), class_certainty (best_choice)); +} + + +/********************************************************************** + * permute_compound_word + * + * Return the top choice for each character as the choice for the word. + **********************************************************************/ +A_CHOICE *permute_compound_words(CHOICES_LIST character_choices, + float rating_limit) { + A_CHOICE *first_choice; + A_CHOICE *best_choice = NULL; + char word[MAX_WERD_LENGTH + 1]; + float rating = 0; + float certainty = 10000; + char char_choice; + int x; + int first_index = 0; + char *ptr; + + word[0] = '\0'; + + if (array_count (character_choices) > MAX_WERD_LENGTH) { + return (new_choice (NULL, MAX_FLOAT32, -MAX_FLOAT32, -1, NO_PERM)); + } + + array_loop(character_choices, x) { + + first_choice = + (A_CHOICE *) first ((CHOICES) array_value (character_choices, x)); + + ptr = class_string (first_choice); + char_choice = ptr != NULL ? *ptr : '\0'; + if (x > first_index && (char_choice == '-' || char_choice == '/')) { + if (compound_debug) + cprintf ("Hyphenated word found\n"); + + permute_subword (character_choices, rating_limit, + first_index, x - 1, word, &rating, &certainty); + + if (rating > rating_limit) + break; + first_index = x + 1; + strcat (word, class_string (first_choice)); + rating += class_probability (first_choice); + certainty = min (class_certainty (first_choice), certainty); + } + } + + if (first_index > 0 && first_index < x && rating <= rating_limit) { + permute_subword (character_choices, rating_limit, + first_index, x - 1, word, &rating, &certainty); + + best_choice = new_choice (word, rating, certainty, -1, COMPOUND_PERM); + } + return (best_choice); +} + + +/********************************************************************** + * permute_subword + * + * Permute a part of a compound word this subword is bounded by hyphens + * and the start and end of the word. Call the standard word permute + * function on a set of choices covering only part of the original + * word. When it is done reclaim the memory that was used in the + * excercise. + **********************************************************************/ +void permute_subword(CHOICES_LIST character_choices, + float rating_limit, + int start, + int end, + char *word, + float *rating, + float *certainty) { + int x; + A_CHOICE *best_choice = NULL; + A_CHOICE raw_choice; + CHOICES_LIST subchoices; + CHOICES choices; + char this_char; + char *ptr; + + DisableChoiceAccum(); + raw_choice.string = NULL; + raw_choice.rating = MAX_INT16; + raw_choice.certainty = -MAX_INT16; + + subchoices = new_choice_list (); + for (x = start; x <= end; x++) { + choices = (CHOICES) array_value (character_choices, x); + ptr = best_string (choices); + this_char = ptr != NULL ? *ptr : '\0'; + if (this_char != '-' && this_char != '/') { + subchoices = array_push (subchoices, choices); + } else { + const char* str = best_string(choices); + strcat (word, str); + } + } + + if (array_count (subchoices)) { + if (compound_debug) + dawg_debug = TRUE; + best_choice = permute_all (subchoices, rating_limit, &raw_choice); + if (compound_debug) + dawg_debug = FALSE; + + if (best_choice && class_string (best_choice)) { + strcat (word, class_string (best_choice)); + *rating += class_probability (best_choice); + *certainty = min (class_certainty (best_choice), *certainty); + } + else { + *rating = MAX_FLOAT32; + } + } + else { + *rating = MAX_FLOAT32; + } + + free_choice_list(subchoices); + if (best_choice) + free_choice(best_choice); + + if (compound_debug && *rating < MAX_FLOAT32) { + cprintf ("Subword permuted = %s, %5.2f, %5.2f\n\n", + word, *rating, *certainty); + } + if (raw_choice.string) + strfree(raw_choice.string); + + EnableChoiceAccum(); +} + + +/********************************************************************** + * permute_top_choice + * + * Return the top choice for each character as the choice for the word. + * In addition a choice is created for the best lower and upper case + * non-words. In each character position the best lower (or upper) case + * character is substituted for the best overall character. + **********************************************************************/ +A_CHOICE *permute_top_choice(CHOICES_LIST character_choices, + float rating_limit, + A_CHOICE *raw_choice, + BOOL8 *any_alpha) { + CHOICES char_list; + A_CHOICE *first_choice; + A_CHOICE *best_choice; + A_CHOICE *other_choice; + char *ptr; + char first_char; //first choice + char second_char; //second choice + char third_char; //third choice + char prev_char = '\0'; //prev in word + char next_char = '\0'; //next in word + char next_next_char = '\0'; //after next next in word + + char word[MAX_PERM_LENGTH + 1]; + char capital_word[MAX_PERM_LENGTH + 1]; + char lower_word[MAX_PERM_LENGTH + 1]; + int x; + BOOL8 char_alpha; + + float rating = 0; + float upper_rating = 0; + float lower_rating = 0; + float first_rating = 0; + + float certainty = 10000; + float upper_certainty = 10000; + float lower_certainty = 10000; + + float certainties[MAX_PERM_LENGTH + 1]; + float lower_certainties[MAX_PERM_LENGTH + 1]; + float upper_certainties[MAX_PERM_LENGTH + 1]; + + register CHOICES this_char; + register char ch; + register INT8 lower_done; + register INT8 upper_done; + + if (any_alpha != NULL) + *any_alpha = FALSE; + + if (array_count (character_choices) > MAX_PERM_LENGTH) { + return (NULL); + } + + array_loop(character_choices, x) { + if (x + 1 < array_count (character_choices)) { + char_list = (CHOICES) array_value (character_choices, x + 1); + first_choice = (A_CHOICE *) first (char_list); + + ptr = class_string (first_choice); + next_char = (ptr != NULL && *ptr != '\0') ? *ptr : ' '; + } + else + next_char = '\0'; + if (x + 2 < array_count (character_choices)) { + char_list = (CHOICES) array_value (character_choices, x + 2); + first_choice = (A_CHOICE *) first (char_list); + + ptr = class_string (first_choice); + next_next_char = (ptr != NULL && *ptr != '\0') ? *ptr : ' '; + } + else + next_next_char = '\0'; + + char_list = (CHOICES) array_value (character_choices, x); + first_choice = (A_CHOICE *) first (char_list); + + ptr = class_string (first_choice); + word[x] = (ptr != NULL && *ptr != '\0') ? *ptr : ' '; + + lower_word[x] = word[x]; + capital_word[x] = word[x]; + first_char = word[x]; + first_rating = class_probability (first_choice); + upper_rating += class_probability (first_choice); + lower_rating += class_probability (first_choice); + lower_certainty = min (class_certainty (first_choice), lower_certainty); + upper_certainty = min (class_certainty (first_choice), upper_certainty); + + certainties[x] = class_certainty (first_choice); + lower_certainties[x] = class_certainty (first_choice); + upper_certainties[x] = class_certainty (first_choice); + + lower_done = FALSE; + upper_done = FALSE; + char_alpha = FALSE; + second_char = '\0'; + third_char = '\0'; + iterate_list(this_char, char_list) { + ptr = best_string (this_char); + ch = ptr != NULL ? *ptr : '\0'; + if (ch == 'l' && rest (this_char) != NULL + && best_probability (rest (this_char)) == first_rating) { + ptr = best_string (rest (this_char)); + if (ptr != NULL && (*ptr == '1' || *ptr == 'I')) { + second_char = *ptr; + this_char = rest (this_char); + if (rest (this_char) != NULL + && best_probability (rest (this_char)) == first_rating) { + ptr = best_string (rest (this_char)); + if (ptr != NULL && (*ptr == '1' || *ptr == 'I')) { + third_char = *ptr; + this_char = rest (this_char); + } + } + ch = choose_il1 (first_char, second_char, third_char, + prev_char, next_char, next_next_char); + if (ch != 'l' && word[x] == 'l') { + word[x] = ch; + lower_word[x] = ch; + capital_word[x] = ch; + } + } + } + /* Find lower case */ + if (!lower_done && (islower (ch) || (isupper (ch) && x == 0))) { + lower_word[x] = ch; + lower_rating += best_probability (this_char); + lower_rating -= class_probability (first_choice); + lower_certainty = min (best_certainty (this_char), lower_certainty); + lower_certainties[x] = best_certainty (this_char); + lower_done = TRUE; + } + /* Find upper case */ + if (!upper_done && isupper (ch)) { + capital_word[x] = ch; + upper_rating += best_probability (this_char); + upper_rating -= class_probability (first_choice); + upper_certainty = min (best_certainty (this_char), upper_certainty); + upper_certainties[x] = best_certainty (this_char); + upper_done = TRUE; + } + if (!char_alpha && isalpha (ch)) + char_alpha = TRUE; + if (lower_done && upper_done) + break; + } + if (char_alpha && any_alpha != NULL) + *any_alpha = TRUE; + + if (first_choice == NULL) { + cprintf ("Permuter giving up due to null choices list"); + word[x + 1] = '$'; + word[x + 2] = '\0'; + cprintf (" word=%s\n", word); + return (NULL); + } + + rating += class_probability (first_choice); + if (rating > rating_limit) + return (NULL); + + certainty = min (class_certainty (first_choice), certainty); + prev_char = word[x]; + } + + lower_word[x] = '\0'; + capital_word[x] = '\0'; + word[x] = '\0'; + + if (rating < class_probability (raw_choice)) { + if (class_string (raw_choice)) + strfree (class_string (raw_choice)); + + class_probability (raw_choice) = rating; + class_certainty (raw_choice) = certainty; + class_string (raw_choice) = strsave (word); + class_permuter (raw_choice) = TOP_CHOICE_PERM; + + LogNewRawChoice (raw_choice, 1.0, certainties); + } + + best_choice = new_choice (word, rating, certainty, -1, TOP_CHOICE_PERM); + adjust_non_word(best_choice, certainties); + + other_choice = new_choice (lower_word, lower_rating, lower_certainty, + -1, LOWER_CASE_PERM); + adjust_non_word(other_choice, lower_certainties); + if (class_probability (best_choice) > class_probability (other_choice)) { + clone_choice(best_choice, other_choice); + } + free_choice(other_choice); + + other_choice = new_choice (capital_word, upper_rating, upper_certainty, + -1, UPPER_CASE_PERM); + adjust_non_word(other_choice, upper_certainties); + if (class_probability (best_choice) > class_probability (other_choice)) { + clone_choice(best_choice, other_choice); + } + free_choice(other_choice); + + return (best_choice); +} + + +/********************************************************************** + * choose_il1 + * + * Choose between the candidate il1 chars. + **********************************************************************/ +char choose_il1(char first_char, //first choice + char second_char, //second choice + char third_char, //third choice + char prev_char, //prev in word + char next_char, //next in word + char next_next_char) { //after next next in word + INT32 type1; //1/I/l type of first choice + INT32 type2; //1/I/l type of second choice + INT32 type3; //1/I/l type of third choice + + if (first_char == 'l' && second_char != '\0') { + if (second_char == 'I' + && (isupper (prev_char) && !islower (next_char) + && !isdigit (next_char) || isupper (next_char) + && !islower (prev_char) && !isdigit (prev_char))) + first_char = second_char; //override + else if (second_char == '1' || third_char == '1') { + if (isdigit (next_char) || isdigit (prev_char) + || next_char == 'l' && isdigit (next_next_char)) { + first_char = '1'; + } + else if (!islower (prev_char) + && (!islower (next_char) || next_char == 's' + && next_next_char == 't')) { + if ((prev_char != '\'' && prev_char != '`' || next_char != '\0') + && (next_char != '\'' && next_char != '`' + || prev_char != '\0')) { + first_char = '1'; + } + } + } + if (first_char == 'l' && next_char != '\0' && !isalpha (prev_char)) { + type1 = 2; + + if (second_char == '1') + type2 = 0; + else if (second_char == 'I') + type2 = 1; + else if (second_char == 'l') + type2 = 2; + else + type2 = type1; + + if (third_char == '1') + type3 = 0; + else if (third_char == 'I') + type3 = 1; + else if (third_char == 'l') + type3 = 2; + else + type3 = type1; + + if (bigram_counts[next_char][type2] > + bigram_counts[next_char][type1]) { + first_char = second_char; + type1 = type2; + } + if (bigram_counts[next_char][type3] > + bigram_counts[next_char][type1]) { + first_char = third_char; + } + } + } + return first_char; +} + + +/********************************************************************** + * permute_words + * + * Permute all the characters together using the dawg to prune all + * but the valid words. + **********************************************************************/ +A_CHOICE *permute_words(CHOICES_LIST char_choices, float rating_limit) { + A_CHOICE *best_choice; + int hyphen_len; + + best_choice = new_choice (NULL, rating_limit, -MAX_FLOAT32, -1, NO_PERM); + + hyphen_len = hyphen_string != NULL ? strlen (hyphen_string) : 0; + if (hyphen_len + array_count (char_choices) > MAX_WERD_LENGTH) { + class_probability (best_choice) = MAX_FLOAT32; + } + else { + + dawg_permute_and_select ("system words:", word_dawg, SYSTEM_DAWG_PERM, + char_choices, best_choice, TRUE); + + dawg_permute_and_select ("document_words", document_words, + DOC_DAWG_PERM, char_choices, best_choice, + FALSE); + + dawg_permute_and_select ("user words", user_words, USER_DAWG_PERM, + char_choices, best_choice, FALSE); + case_sensative = FALSE; + } + + return (best_choice); +} + + +/********************************************************************** + * valid_word + * + * Check all the DAWGs to see if this word is in any of them. + **********************************************************************/ +int valid_word(const char *string) { + int result = NO_PERM; + + if (word_in_dawg (word_dawg, string)) + result = SYSTEM_DAWG_PERM; + else { + if (word_in_dawg (document_words, string)) + result = DOC_DAWG_PERM; + else if (word_in_dawg (user_words, string)) + result = USER_DAWG_PERM; + case_sensative = FALSE; + } + return (result); +} diff --git a/dict/permute.h b/dict/permute.h new file mode 100644 index 0000000000..d7ae6caa01 --- /dev/null +++ b/dict/permute.h @@ -0,0 +1,91 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permute.h (Formerly permute.h) + * Description: Permute choices together + * Author: Mark Seaman, OCR Technology + * Created: Fri Sep 22 14:05:51 1989 + * Modified: Mon May 20 16:32:04 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ********************************************************************************/ +#ifndef PERMUTE_H +#define PERMUTE_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "choicearr.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define RATING_PAD 4.0 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int adjust_debug; +extern float garbage; +extern float non_word; +extern int permute_only_top; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void add_document_word(A_CHOICE *best_choice); + +void adjust_non_word (A_CHOICE * best_choice, float certainties[]); + +void init_permute(); +void end_permute(); + +A_CHOICE *permute_all(CHOICES_LIST char_choices, + float rating_limit, + A_CHOICE *raw_choice); + +void permute_characters(CHOICES_LIST char_choices, + float limit, + A_CHOICE *best_choice, + A_CHOICE *raw_choice); + +A_CHOICE *permute_compound_words(CHOICES_LIST character_choices, + float rating_limit); + +void permute_subword(CHOICES_LIST character_choices, + float rating_limit, + int start, + int end, + char *word, + float *rating, + float *certainty); + +A_CHOICE *permute_top_choice(CHOICES_LIST character_choices, + float rating_limit, + A_CHOICE *raw_choice, + BOOL8 *any_alpha); + +char choose_il1(char first_char, //first choice + char second_char, //second choice + char third_char, //third choice + char prev_char, //prev in word + char next_char, //next in word + char next_next_char); + +A_CHOICE *permute_words(CHOICES_LIST char_choices, float rating_limit); + +int valid_word(const char *string); +#endif diff --git a/dict/states.cpp b/dict/states.cpp new file mode 100644 index 0000000000..bd7fa9f85e --- /dev/null +++ b/dict/states.cpp @@ -0,0 +1,382 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: states.c (Formerly states.c) + * Description: Representations of search states + * Author: Mark Seaman, OCR Technology + * Created: Wed May 16 15:49:34 1990 + * Modified: Mon Jun 17 17:54:41 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "states.h" +#include "structures.h" +#include "tordvars.h" +#include "callcpp.h" + +/*------------------------------------------------------------------------- + Variables +--------------------------------------------------------------------------*/ +#define STATEBLOCK 100 /* Cells per block */ +makestructure (newstate, free_state, printstate, STATE, +freestate, STATEBLOCK, "STATE", statecount); + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * bin_to_chunks + * + * Convert a representation of the search state in "STATE" form to one + * in "SEARCH_STATE" form. Create the memory required to hold the + * resultant state value. + **********************************************************************/ +SEARCH_STATE bin_to_chunks(STATE *state, int num_joints) { + int x; + unsigned int mask; + int depth; + int pieces = 0; + SEARCH_STATE s; + + s = memalloc (sizeof (int) * (ones_in_state (state, num_joints) + 1)); + + depth = 1; + mask = 1 << (num_joints - 1 - 32); + for (x = num_joints; x > 32; x--) { + if (state->part1 & mask) { + s[depth++] = pieces; + pieces = 0; + } + else { + pieces++; + } + mask >>= 1; + } + + if (num_joints > 32) + mask = 1 << 31; + else + mask = 1 << (num_joints - 1); + + while (x--) { + if (state->part2 & mask) { + s[depth++] = pieces; + pieces = 0; + } + else { + pieces++; + } + mask >>= 1; + } + s[0] = depth - 1; + + return (s); +} + + +/********************************************************************** + * bin_to_pieces + * + * Convert the binary (bit vector) format of a search state to an array + * of piece counts. This array has a zero element after the last valid + * character. + **********************************************************************/ +void bin_to_pieces(STATE *state, int num_joints, PIECES_STATE pieces) { + int x; + unsigned int mask; /* Bit mask */ + INT16 num_pieces = 0; + /* Preset mask */ + if (debug_8) + print_state ("bin_to_pieces = ", state, num_joints); + + mask = ((num_joints > 32) ? + (1 << (num_joints - 1 - 32)) : (1 << (num_joints - 1))); + + pieces[num_pieces] = 0; + + for (x = num_joints - 1; x >= 0; x--) { + /* Iterate all bits */ + pieces[num_pieces]++; + + if ((x < 32) ? /* Test for 1 bit */ + ((state->part2 & mask) ? TRUE : FALSE) : + ((state->part1 & mask) ? TRUE : FALSE)) { + pieces[++num_pieces] = 0; + if (debug_8) + cprintf ("[%d]=%d ", num_pieces - 1, pieces[num_pieces - 1]); + } + /* Next mask value */ + mask = ((mask == 1) ? (1 << 31) : (mask >> 1)); + } + pieces[num_pieces]++; + pieces[++num_pieces] = 0; + ASSERT_HOST (num_pieces < MAX_NUM_CHUNKS + 2); + if (debug_8) + new_line(); +} + + +/********************************************************************** + * insert_new_chunk + * + * Add a new chunk division into this state vector at the location + * requested. + **********************************************************************/ +void insert_new_chunk(register STATE *state, + register int index, + register int num_joints) { + register unsigned int mask; + register unsigned int result; + + index = (num_joints - index); + if (index < 32) { + mask = ~0; + mask <<= index; + result = (mask & state->part2) << 1; + result |= (~mask & state->part2); + state->part1 <<= 1; + if (state->part2 & 0x80000000) + state->part1 |= 1; + state->part2 = result; + } + else { + mask = ~0; + mask <<= index - 32; + result = (mask & state->part1) << 1; + result |= (~mask & state->part1); + state->part1 = result; + } +} + + +/********************************************************************** + * new_state + * + * Create a memory space for a new state variable. Set its initial + * value according to the parameters. + **********************************************************************/ +STATE *new_state(STATE *oldstate) { + STATE *this_state; + + this_state = newstate (); + this_state->part1 = oldstate->part1; + this_state->part2 = oldstate->part2; + return (this_state); +} + + +/********************************************************************* + * ones_in_state + * + * Return the number of ones that are in this state. + **********************************************************************/ +int ones_in_state(STATE *state, int num_joints) { + INT8 num_ones = 0; + INT8 x; + unsigned int mask; + + if (num_joints > 32) /* Preset mask */ + mask = 1 << (num_joints - 1 - 32); + else + mask = 1 << (num_joints - 1); + + for (x = num_joints - 1; x >= 0; x--) { + /* Iterate all bits */ + + if (x < 32) + num_ones += ((state->part2 & mask) ? 1 : 0); + else + num_ones += ((state->part1 & mask) ? 1 : 0); + + if (mask == 1) /* Next mask value */ + mask = 1 << 31; + else + mask >>= 1; + } + + return (num_ones); +} + + +/********************************************************************** + * print_state + * + * Print out the current state variable on a line with a label. + **********************************************************************/ +void print_state(const char *label, STATE *state, int num_joints) { + int x; + unsigned int mask; /* Bit mask */ + + if (num_joints > 32) /* Preset mask */ + mask = 1 << (num_joints - 1 - 32); + else + mask = 1 << (num_joints - 1); + + cprintf ("%s ", label); + + for (x = num_joints - 1; x >= 0; x--) { + /* Iterate all bits */ + + if (x < 32) + cprintf ("%d", ((state->part2 & mask) ? 1 : 0)); + else + cprintf ("%d", ((state->part1 & mask) ? 1 : 0)); + if (x % 4 == 0) + cprintf (" "); + + if (mask == 1) /* Next mask value */ + mask = 1 << 31; + else + mask >>= 1; + } + + new_line(); +} + + +/********************************************************************** + * set_n_ones + * + * Set the first n bits in a state. + **********************************************************************/ +void set_n_ones(STATE *state, int n) { + if (n < 32) { + state->part2 = ~0; + state->part2 >>= 32 - n; + state->part1 = 0; + } + else { + state->part2 = ~0; + state->part1 = ~0; + state->part1 >>= 64 - n; + } +} + + +/********************************************************************** + * compare_states + * + * Compare the 2 states at the given blob index. Return 1 if the given + * blob is a fragment compared to reality, 2 if correct, 4 if a join, + * and 5 if both a join and a fragment. + * On return the blob index is set to the corresponding index in the + * correct string. + **********************************************************************/ +int compare_states(STATE *true_state, STATE *this_state, int *blob_index) { + int blob_count; //number found + int true_index; //index of true blob + int index; //current + int result = 0; //return value + UINT32 mask; + + if (true_state->part1 == this_state->part1 + && true_state->part2 == this_state->part2) + return 2; + if (*blob_index == 0) { + if (bits_in_states > 32) { + for (mask = 1 << bits_in_states - 33; mask != 0; mask >>= 1) { + if (this_state->part1 & mask) { + if (true_state->part1 & mask) + return 2; + else + return 1; + } + else if (true_state->part1 & mask) + return 4; + } + index = 31; + } + else + index = bits_in_states - 1; + for (mask = 1 << index; mask != 0; mask >>= 1) { + if (this_state->part2 & mask) { + if (true_state->part2 & mask) + return 2; + else + return 1; + } + else if (true_state->part2 & mask) + return 4; + } + return 2; + } + else { + blob_count = 0; + true_index = 0; + if (bits_in_states > 32) { + for (mask = 1 << bits_in_states - 33; mask != 0; mask >>= 1) { + if (true_state->part1 & mask) + true_index++; + if (this_state->part1 & mask) { + blob_count++; + if (blob_count == *blob_index) { + if ((true_state->part1 & mask) == 0) + result = 1; + break; + } + } + } + if (blob_count == *blob_index) { + for (mask >>= 1; mask != 0; mask >>= 1) { + if (this_state->part1 & mask) { + if ((true_state->part1 & mask) && result == 0) + return 2; + else + return result | 1; + } + else if (true_state->part1 & mask) + result |= 4; + } + } + index = 31; + } + else + index = bits_in_states - 1; + mask = 1 << index; + if (blob_count < *blob_index) { + for (; mask != 0; mask >>= 1) { + if (true_state->part2 & mask) + true_index++; + if (this_state->part2 & mask) { + blob_count++; + if (blob_count == *blob_index) { + if ((true_state->part2 & mask) == 0) + result = 1; + break; + } + } + } + if (blob_count != *blob_index) + return 2; + mask >>= 1; + } + *blob_index = true_index; + for (; mask != 0; mask >>= 1) { + if (this_state->part2 & mask) { + if ((true_state->part2 & mask) && result == 0) + return 2; + else + return result | 1; + } + else if (true_state->part2 & mask) + result |= 4; + } + return result == 0 ? 2 : result; + } +} diff --git a/dict/states.h b/dict/states.h new file mode 100644 index 0000000000..7d01d9745d --- /dev/null +++ b/dict/states.h @@ -0,0 +1,111 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: states.h (Formerly states.h) + * Description: Representations of search states + * Author: Mark Seaman, OCR Technology + * Created: Wed May 16 15:52:40 1990 + * Modified: Tue May 21 16:26:21 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef STATES_H +#define STATES_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "general.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define MAX_NUM_CHUNKS 64 /* Limit on pieces */ + +typedef struct +{ + UINT32 part1; + UINT32 part2; +} STATE; + +typedef int *SEARCH_STATE; /* State variable for search */ + + /* State variable for search */ +typedef UINT8 PIECES_STATE[MAX_NUM_CHUNKS + 2]; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +SEARCH_STATE bin_to_chunks(STATE *state, int num_joints); + +void bin_to_pieces(STATE *state, int num_joints, PIECES_STATE pieces); + +void insert_new_chunk(register STATE *state, + register int index, + int num_joints); + +STATE *new_state(STATE *oldstate); + +int ones_in_state(STATE *state, int num_joints); + +void print_state(const char *label, STATE *state, int num_joints); + +void set_n_ones(STATE *state, int n); + +int compare_states(STATE *true_state, STATE *this_state, int *blob_index); + +extern void free_state(STATE *); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* states.c +void insert_new_chunk + _ARGS((STATE *state, + int index)); + +SEARCH_STATE bin_to_chunks + _ARGS((STATE *state, + int num_joints)); + +STATE *new_state + _ARGS((STATE *oldstate)); + +int ones_in_state + _ARGS((STATE *state, + int num_joints)); + +void print_state + _ARGS((char *label, + STATE *state, + int num_joints)); + +void set_n_ones + _ARGS((STATE *state, + int n)); +int compare_states + _ARGS(( +STATE *true_state, +STATE *this_state, +int* blob_index)); + +#undef _ARGS +*/ +#endif diff --git a/dict/stopper.cpp b/dict/stopper.cpp new file mode 100644 index 0000000000..63e4753d12 --- /dev/null +++ b/dict/stopper.cpp @@ -0,0 +1,1360 @@ +/****************************************************************************** + ** Filename: stopper.c + ** Purpose: Stopping criteria for word classifier. + ** Author: Dan Johnson + ** History: Mon Apr 29 14:56:49 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "stopper.h" +#include "emalloc.h" +#include "matchdefs.h" +#include "debug.h" +#include "callcpp.h" +#include "permute.h" +#include "context.h" +#include "permnum.h" +#include "danerror.h" +#include "const.h" +#include "freelist.h" +#include "efio.h" +#include "globals.h" +#include "scanutils.h" + +#include +#include +#include +#include +#ifdef __UNIX__ +#include +#endif + +/* these are kludges - add appropriate .h file later */ +extern float CertaintyScale; /* from subfeat.h */ + +#define MAX_WERD_SIZE 100 +#define MAX_AMBIG_SIZE 3 +#define DANGEROUS_AMBIGS "tessdata/DangAmbigs" + +typedef LIST AMBIG_TABLE; + +typedef struct +{ + UINT8 Class; + UINT16 NumChunks; + float Certainty; +} + + +CHAR_CHOICE; + +typedef struct +{ + float Rating; + float Certainty; + FLOAT32 AdjustFactor; + int Length; + CHAR_CHOICE Blob[1]; +} VIABLE_CHOICE_STRUCT; +typedef VIABLE_CHOICE_STRUCT *VIABLE_CHOICE; + +typedef struct +{ + VIABLE_CHOICE Choice; + float ChunkCertainty[MAX_NUM_CHUNKS]; + UINT8 ChunkClass[MAX_NUM_CHUNKS]; +} + + +EXPANDED_CHOICE; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +#define BestCertainty(Choices) (((VIABLE_CHOICE) first (Choices))->Certainty) +#define BestRating(Choices) (((VIABLE_CHOICE) first (Choices))->Rating) +#define BestFactor(Choices) (((VIABLE_CHOICE) first (Choices))->AdjustFactor) + +#define AmbigThreshold(F1,F2) (((F2) - (F1)) * AmbigThresholdGain - \ + AmbigThresholdOffset) + +/*--------------------------------------------------------------------------- + Private Function Prototoypes +----------------------------------------------------------------------------*/ +void AddNewChunk(VIABLE_CHOICE Choice, int Blob); + +int AmbigsFound(char *Word, + char *CurrentChar, + const char *Tail, + LIST Ambigs, + DANGERR *fixpt); + +int ChoiceSameAs(A_CHOICE *Choice, VIABLE_CHOICE ViableChoice); + +int CmpChoiceRatings(void *arg1, //VIABLE_CHOICE Choice1, + void *arg2); //VIABLE_CHOICE Choice2); + +void ExpandChoice(VIABLE_CHOICE Choice, EXPANDED_CHOICE *ExpandedChoice); + +AMBIG_TABLE *FillAmbigTable(); + +int FreeBadChoice(void *item1, //VIABLE_CHOICE Choice, + void *item2); //EXPANDED_CHOICE *BestChoice); + +int LengthOfShortestAlphaRun(register char *Word); + +VIABLE_CHOICE NewViableChoice (A_CHOICE * Choice, +FLOAT32 AdjustFactor, float Certainties[]); + +void PrintViableChoice(FILE *File, const char *Label, VIABLE_CHOICE Choice); + +void ReplaceDuplicateChoice (VIABLE_CHOICE OldChoice, +A_CHOICE * NewChoice, +FLOAT32 AdjustFactor, float Certainties[]); + +int StringSameAs(const char *String, VIABLE_CHOICE ViableChoice); + +int UniformCertainties(CHOICES_LIST Choices, A_CHOICE *BestChoice); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* Name of file containing potentially dangerous ambiguities */ +static const char *DangerousAmbigs = DANGEROUS_AMBIGS; + +/* Word for which stopper debug information should be printed to stdout */ +static char *WordToDebug = NULL; + +/* flag used to disable accumulation of word choices during compound word + permutation */ +BOOL8 KeepWordChoices = TRUE; + +/* additional certainty padding allowed before a word is rejected */ +static FLOAT32 RejectOffset = 0.0; + +/* structures to keep track of viable word choices */ +static VIABLE_CHOICE BestRawChoice = NULL; +static LIST BestChoices = NIL; +static PIECES_STATE CurrentSegmentation; + +make_float_var (NonDictCertainty, -2.50, MakeNonDictCertainty, +17, 2, SetNonDictCertainty, +"Certainty threshold for non-dict words"); + +make_float_var (RejectCertaintyOffset, 1.0, MakeRejectCertaintyOffset, +17, 3, SetRejectCertaintyOffset, "Reject certainty offset"); + +make_int_var (SmallWordSize, 2, MakeSmallWordSize, +17, 4, SetSmallWordSize, +"Size of dict word to be treated as non-dict word"); + +make_float_var (CertaintyPerChar, -0.50, MakeCertaintyPerChar, +17, 5, SetCertaintyPerChar, +"Certainty to add for each dict char above SmallWordSize"); + +make_float_var (CertaintyVariation, 3.0, MakeCertaintyVariation, +17, 6, SetCertaintyVariation, +"Max certaintly variation allowed in a word (in sigma)"); + +make_int_var (StopperDebugLevel, 0, MakeStopperDebugLevel, +17, 7, SetStopperDebugLevel, "Stopper debug level"); + +make_float_var (AmbigThresholdGain, 8.0, MakeAmbigThresholdGain, +17, 8, SetAmbigThresholdGain, +"Gain factor for ambiguity threshold"); + +make_float_var (AmbigThresholdOffset, 1.5, MakeAmbigThresholdOffset, +17, 9, SetAmbigThresholdOffset, +"Certainty offset for ambiguity threshold"); + +//extern char *demodir; +extern int first_pass; +INT_VAR (tessedit_truncate_wordchoice_log, 10, "Max words to keep in list"); + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int AcceptableChoice(CHOICES_LIST Choices, + A_CHOICE *BestChoice, + A_CHOICE *RawChoice, + DANGERR *fixpt) { +/* + ** Parameters: + ** Choices choices for current segmentation + ** BestChoice best choice for current segmentation + ** RawChoice best raw choice for current segmentation + ** Globals: + ** NonDictCertainty certainty for a non-dict word + ** SmallWordSize size of word to be treated as non-word + ** CertaintyPerChar certainty to add for each dict char + ** Operation: Return TRUE if the results from this segmentation are + ** good enough to stop. Otherwise return FALSE. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Mon Apr 29 14:57:32 1991, DSJ, Created. + */ + float CertaintyThreshold = NonDictCertainty; + int WordSize; + + if (fixpt != NULL) + fixpt->index = -1; + if ((BestChoice == NULL) || (class_string (BestChoice) == NULL)) + return (FALSE); + + if (StopperDebugLevel >= 1) + cprintf ("\nStopper: %s (word=%c, case=%c, punct=%c)\n", + class_string (BestChoice), + (valid_word (class_string (BestChoice)) ? 'y' : 'n'), + (case_ok (class_string (BestChoice)) ? 'y' : 'n'), + ((punctuation_ok (class_string (BestChoice)) != + -1) ? 'y' : 'n')); + + if (valid_word (class_string (BestChoice)) && + case_ok (class_string (BestChoice)) && + punctuation_ok (class_string (BestChoice)) != -1) { + WordSize = LengthOfShortestAlphaRun (class_string (BestChoice)); + WordSize -= SmallWordSize; + if (WordSize < 0) + WordSize = 0; + CertaintyThreshold += WordSize * CertaintyPerChar; + } + else if (stopper_numbers_on && valid_number (class_string (BestChoice))) { + CertaintyThreshold += stopper_numbers_on * CertaintyPerChar; + } + + if (StopperDebugLevel >= 1) + cprintf ("Stopper: Certainty = %4.1f, Threshold = %4.1f\n", + class_certainty (BestChoice), CertaintyThreshold); + + if (NoDangerousAmbig (class_string (BestChoice), fixpt) + && class_certainty (BestChoice) > CertaintyThreshold && + UniformCertainties (Choices, BestChoice)) + return (TRUE); + else + return (FALSE); + +} /* AcceptableChoice */ + + +/*---------------------------------------------------------------------------*/ +int AcceptableResult(A_CHOICE *BestChoice, A_CHOICE *RawChoice) { +/* + ** Parameters: + ** BestChoice best choice for current word + ** RawChoice best raw choice for current word + ** Globals: + ** NonDictCertainty certainty for a non-dict word + ** SmallWordSize size of word to be treated as non-word + ** CertaintyPerChar certainty to add for each dict char + ** BestChoices list of all good choices found + ** RejectOffset allowed offset before a word is rejected + ** Operation: Return FALSE if the best choice for the current word + ** is questionable and should be tried again on the second + ** pass or should be flagged to the user. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Thu May 9 14:05:05 1991, DSJ, Created. + */ + float CertaintyThreshold = NonDictCertainty - RejectOffset; + int WordSize; + + if (StopperDebugLevel >= 1) + cprintf ("\nRejecter: %s (word=%c, case=%c, punct=%c, unambig=%c)\n", + class_string (BestChoice), + (valid_word (class_string (BestChoice)) ? 'y' : 'n'), + (case_ok (class_string (BestChoice)) ? 'y' : 'n'), + ((punctuation_ok (class_string (BestChoice)) != -1) ? 'y' : 'n'), + ((rest (BestChoices) != NIL) ? 'n' : 'y')); + + if ((BestChoice == NULL) || + (class_string (BestChoice) == NULL) || CurrentWordAmbig ()) + return (FALSE); + + if (valid_word (class_string (BestChoice)) && + case_ok (class_string (BestChoice)) && + punctuation_ok (class_string (BestChoice)) != -1) { + WordSize = LengthOfShortestAlphaRun (class_string (BestChoice)); + WordSize -= SmallWordSize; + if (WordSize < 0) + WordSize = 0; + CertaintyThreshold += WordSize * CertaintyPerChar; + } + + if (StopperDebugLevel >= 1) + cprintf ("Rejecter: Certainty = %4.1f, Threshold = %4.1f ", + class_certainty (BestChoice), CertaintyThreshold); + + if (class_certainty (BestChoice) > CertaintyThreshold) { + if (StopperDebugLevel >= 1) + cprintf ("ACCEPTED\n"); + return (TRUE); + } + else { + if (StopperDebugLevel >= 1) + cprintf ("REJECTED\n"); + return (FALSE); + } +} /* AcceptableResult */ + + +/*---------------------------------------------------------------------------*/ +int AlternativeChoicesWorseThan(FLOAT32 Threshold) { +/* + ** Parameters: + ** Threshold minimum adjust factor for alternative choices + ** Globals: + ** BestChoices alternative choices for current word + ** Operation: This routine returns TRUE if there are no alternative + ** choices for the current word OR if all alternatives have + ** an adjust factor worse than Threshold. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Mon Jun 3 09:36:31 1991, DSJ, Created. + */ + LIST Alternatives; + VIABLE_CHOICE Choice; + + Alternatives = rest (BestChoices); + iterate(Alternatives) { + Choice = (VIABLE_CHOICE) first (Alternatives); + if (Choice->AdjustFactor <= Threshold) + return (FALSE); + } + + return (TRUE); + +} /* AlternativeChoicesWorseThan */ + + +/*---------------------------------------------------------------------------*/ +int CurrentBestChoiceIs(const char *Word) { +/* + ** Parameters: + ** Word string to compare to current best choice + ** Globals: + ** BestChoices set of best choices for current word + ** Operation: Returns TRUE if Word is the same as the current best + ** choice, FALSE otherwise. + ** Return: TRUE or FALSE + ** Exceptions: none + ** History: Thu May 30 14:44:22 1991, DSJ, Created. + */ + return (BestChoices != NIL && + StringSameAs (Word, (VIABLE_CHOICE) first (BestChoices))); + +} /* CurrentBestChoiceIs */ + + +/*---------------------------------------------------------------------------*/ +FLOAT32 CurrentBestChoiceAdjustFactor() { +/* + ** Parameters: none + ** Globals: + ** BestChoices set of best choices for current word + ** Operation: Return the adjustment factor for the best choice for + ** the current word. + ** Return: Adjust factor for current best choice. + ** Exceptions: none + ** History: Thu May 30 14:48:24 1991, DSJ, Created. + */ + VIABLE_CHOICE BestChoice; + + if (BestChoices == NIL) + return (MAX_FLOAT32); + + BestChoice = (VIABLE_CHOICE) first (BestChoices); + return (BestChoice->AdjustFactor); + +} /* CurrentBestChoiceAdjustFactor */ + + +/*---------------------------------------------------------------------------*/ +int CurrentWordAmbig() { +/* + ** Parameters: none + ** Globals: + ** BestChoices set of best choices for current word + ** Operation: This routine returns TRUE if there are multiple good + ** choices for the current word and FALSE otherwise. + ** Return: TRUE or FALSE + ** Exceptions: none + ** History: Wed May 22 15:38:38 1991, DSJ, Created. + */ + return (rest (BestChoices) != NIL); + +} /* CurrentWordAmbig */ + + +/*---------------------------------------------------------------------------*/ +void DebugWordChoices() { +/* + ** Parameters: none + ** Globals: + ** BestRawChoice + ** BestChoices + ** Operation: Print the current choices for this word to stdout. + ** Return: none + ** Exceptions: none + ** History: Wed May 15 13:52:08 1991, DSJ, Created. + */ + LIST Choices; + int i; + char LabelString[80]; + + if (StopperDebugLevel >= 1 || + WordToDebug && BestChoices && + StringSameAs (WordToDebug, (VIABLE_CHOICE) first (BestChoices))) { + if (BestRawChoice) + PrintViableChoice (stdout, "\nBest Raw Choice: ", BestRawChoice); + + i = 1; + Choices = BestChoices; + if (Choices) + cprintf ("\nBest Cooked Choices:\n"); + iterate(Choices) { + sprintf (LabelString, "Cooked Choice #%d: ", i); + PrintViableChoice (stdout, LabelString, + (VIABLE_CHOICE) first (Choices)); + i++; + } + } +} /* DebugWordChoices */ + + +/*---------------------------------------------------------------------------*/ +void FilterWordChoices() { +/* + ** Parameters: none + ** Globals: + ** BestChoices set of choices for current word + ** Operation: This routine removes from BestChoices all choices which + ** are not within a reasonable range of the best choice. + ** Return: none + ** Exceptions: none + ** History: Wed May 15 13:08:24 1991, DSJ, Created. + */ + EXPANDED_CHOICE BestChoice; + + if (BestChoices == NIL || second (BestChoices) == NIL) + return; + + /* compute certainties and class for each chunk in best choice */ + ExpandChoice ((VIABLE_CHOICE_STRUCT *) first (BestChoices), &BestChoice); + + set_rest (BestChoices, delete_d (rest (BestChoices), + &BestChoice, FreeBadChoice)); + +} /* FilterWordChoices */ + + +/*---------------------------------------------------------------------------*/ +void +FindClassifierErrors (FLOAT32 MinRating, +FLOAT32 MaxRating, +FLOAT32 RatingMargin, FLOAT32 Thresholds[]) { +/* + ** Parameters: + ** MinRating limits how tight to make a template + ** MaxRating limits how loose to make a template + ** RatingMargin amount of margin to put in template + ** Thresholds[] place to put error thresholds + ** Globals: none + ** Operation: This routine compares the best choice for the current + ** word to the best raw choice to determine which characters + ** were classified incorrectly by the classifier. It then + ** places a separate threshold into Thresholds for each + ** character in the word. If the classifier was correct, + ** MaxRating is placed into Thresholds. If the + ** classifier was incorrect, the avg. match rating (error + ** percentage) of the classifier's incorrect choice minus + ** some margin is + ** placed into thresholds. This can then be used by the + ** caller to try to create a new template for the desired + ** class that will classify the character with a rating better + ** than the threshold value. The match rating placed into + ** Thresholds is never allowed to be below MinRating in order + ** to prevent trying to make overly tight templates. + ** Return: none (results are placed in Thresholds) + ** Exceptions: none + ** History: Fri May 31 16:02:57 1991, DSJ, Created. + */ + EXPANDED_CHOICE BestRaw; + VIABLE_CHOICE Choice; + int i, j, Chunk; + FLOAT32 AvgRating; + int NumErrorChunks; + + assert (BestChoices != NIL); + assert (BestRawChoice != NULL); + + ExpandChoice(BestRawChoice, &BestRaw); + Choice = (VIABLE_CHOICE) first (BestChoices); + + for (i = 0, Chunk = 0; i < Choice->Length; i++, Thresholds++) { + AvgRating = 0.0; + NumErrorChunks = 0; + + for (j = 0; j < Choice->Blob[i].NumChunks; j++, Chunk++) + if (Choice->Blob[i].Class != BestRaw.ChunkClass[Chunk]) { + AvgRating += BestRaw.ChunkCertainty[Chunk]; + NumErrorChunks++; + } + + if (NumErrorChunks > 0) { + AvgRating /= NumErrorChunks; + *Thresholds = (AvgRating / -CertaintyScale) * (1.0 - RatingMargin); + } + else + *Thresholds = MaxRating; + + if (*Thresholds > MaxRating) + *Thresholds = MaxRating; + if (*Thresholds < MinRating) + *Thresholds = MinRating; + } +} /* FindClassifierErrors */ + + +/*---------------------------------------------------------------------------*/ +void InitStopperVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Initializes the control variables used by the stopper. + ** Return: none + ** Exceptions: none + ** History: Thu May 9 10:06:04 1991, DSJ, Created. + */ + VALUE dummy; + + string_variable (DangerousAmbigs, "DangerousAmbigs", DANGEROUS_AMBIGS); + string_variable (WordToDebug, "WordToDebug", ""); + + MakeNonDictCertainty(); + MakeRejectCertaintyOffset(); + MakeSmallWordSize(); + MakeCertaintyPerChar(); + MakeCertaintyVariation(); + MakeStopperDebugLevel(); + MakeAmbigThresholdGain(); + MakeAmbigThresholdOffset(); +} /* InitStopperVars */ + + +/*---------------------------------------------------------------------------*/ +void InitChoiceAccum() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine initializes the data structures used to + ** keep track the good word choices found for a word. + ** Return: none + ** Exceptions: none + ** History: Fri May 17 07:59:00 1991, DSJ, Created. + */ + BLOB_WIDTH *BlobWidth, *End; + + if (BestRawChoice) + memfree(BestRawChoice); + + if (BestChoices) + destroy_nodes(BestChoices, memfree); + + BestRawChoice = NULL; + BestChoices = NIL; + EnableChoiceAccum(); + + for (BlobWidth = CurrentSegmentation, + End = CurrentSegmentation + MAX_NUM_CHUNKS; + BlobWidth < End; *BlobWidth++ = 1); + +} /* InitChoiceAccum */ + + +/*---------------------------------------------------------------------------*/ +void +LogNewRawChoice (A_CHOICE * Choice, FLOAT32 AdjustFactor, float Certainties[]) { +/* + ** Parameters: + ** Choice new raw choice for current word + ** AdjustFactor adjustment factor which was applied to choice + ** Certainties certainties for each char in new choice + ** Globals: + ** BestRawChoice best raw choice so far for current word + ** Operation: This routine compares Choice to the best raw (non-dict) + ** choice so far and replaces it if the new choice is better. + ** Return: none + ** Exceptions: none + ** History: Wed May 15 09:57:19 1991, DSJ, Created. + */ + if (!KeepWordChoices) + return; + + if (!BestRawChoice) + BestRawChoice = NewViableChoice (Choice, AdjustFactor, Certainties); + else if (class_probability (Choice) < BestRawChoice->Rating) { + if (ChoiceSameAs (Choice, BestRawChoice)) + ReplaceDuplicateChoice(BestRawChoice, Choice, AdjustFactor, Certainties); + else { + memfree(BestRawChoice); + BestRawChoice = NewViableChoice (Choice, AdjustFactor, Certainties); + } + } +} /* LogNewRawChoice */ + + +/*---------------------------------------------------------------------------*/ +void LogNewSegmentation(PIECES_STATE BlobWidth) { +/* + ** Parameters: + ** BlobWidth[] number of chunks in each blob in segmentation + ** Globals: + ** CurrentSegmentation blob widths for current segmentation + ** Operation: This routine updates the blob widths in CurrentSegmentation + ** to be the same as provided in BlobWidth. + ** Return: none + ** Exceptions: none + ** History: Mon May 20 11:52:26 1991, DSJ, Created. + */ + BLOB_WIDTH *Segmentation; + + for (Segmentation = CurrentSegmentation; *BlobWidth != 0; + BlobWidth++, Segmentation++) + *Segmentation = *BlobWidth; + *Segmentation = 0; + +} /* LogNewSegmentation */ + + +/*---------------------------------------------------------------------------*/ +void LogNewSplit(int Blob) { +/* + ** Parameters: + ** Blob index of blob that was split + ** Globals: + ** BestRawChoice current best raw choice + ** BestChoices list of best choices found so far + ** Operation: This routine adds 1 chunk to the specified blob for each + ** choice in BestChoices and for the BestRawChoice. + ** Return: none + ** Exceptions: none + ** History: Mon May 20 11:38:56 1991, DSJ, Created. + */ + LIST Choices; + + if (BestRawChoice) { + AddNewChunk(BestRawChoice, Blob); + } + + Choices = BestChoices; + iterate(Choices) { + AddNewChunk ((VIABLE_CHOICE) first (Choices), Blob); + } + +} /* LogNewSplit */ + + +/*---------------------------------------------------------------------------*/ +void +LogNewWordChoice (A_CHOICE * Choice, +FLOAT32 AdjustFactor, float Certainties[]) { +/* + ** Parameters: + ** Choice new choice for current word + ** AdjustFactor adjustment factor which was applied to choice + ** Certainties certainties for each char in new choice + ** Globals: + ** BestChoices best choices so far for current word + ** Operation: This routine adds Choice to BestChoices if the + ** adjusted certainty for Choice is within a reasonable range + ** of the best choice in BestChoices. The BestChoices + ** list is kept in sorted order by rating. Duplicates are + ** removed. + ** Return: none + ** Exceptions: none + ** History: Wed May 15 09:57:19 1991, DSJ, Created. + */ + VIABLE_CHOICE NewChoice; + LIST Choices; + FLOAT32 Threshold; + + if (!KeepWordChoices) + return; + + /* throw out obviously bad choices to save some work */ + if (BestChoices != NIL) { + Threshold = AmbigThreshold (BestFactor (BestChoices), AdjustFactor); + if (Threshold > -AmbigThresholdOffset) + Threshold = -AmbigThresholdOffset; + if (class_certainty (Choice) - BestCertainty (BestChoices) < Threshold) + return; + } + + /* see if a choice with the same text string has already been found */ + NewChoice = NULL; + Choices = BestChoices; + iterate(Choices) { + if (ChoiceSameAs (Choice, (VIABLE_CHOICE) first (Choices))) + if (class_probability (Choice) < BestRating (Choices)) + NewChoice = (VIABLE_CHOICE) first (Choices); + else + return; + } + + if (NewChoice) { + ReplaceDuplicateChoice(NewChoice, Choice, AdjustFactor, Certainties); + BestChoices = delete_d (BestChoices, NewChoice, is_same_node); + } + else { + NewChoice = NewViableChoice (Choice, AdjustFactor, Certainties); + } + + BestChoices = s_adjoin (BestChoices, NewChoice, CmpChoiceRatings); + if (StopperDebugLevel >= 2) + PrintViableChoice (stdout, "New Word Choice: ", NewChoice); + if (count (BestChoices) > tessedit_truncate_wordchoice_log) { + Choices = + (LIST) nth_cell (BestChoices, tessedit_truncate_wordchoice_log); + destroy_nodes (rest (Choices), Efree); + set_rest(Choices, NIL); + } + +} /* LogNewWordChoice */ + + +/*---------------------------------------------------------------------------*/ +static AMBIG_TABLE *AmbigFor = NULL; + +int NoDangerousAmbig(const char *Word, DANGERR *fixpt) { +/* + ** Parameters: + ** Word word to check for dangerous ambiguities + ** Globals: none + ** Operation: This word checks each letter in word against a list + ** of potentially ambiguous characters. If a match is found + ** that letter is replaced with its ambiguity and tested in + ** the dictionary. If the ambiguous word is found in the + ** dictionary, FALSE is returned. Otherwise, the search + ** continues for other ambiguities. If no ambiguities that + ** match in the dictionary are found, TRUE is returned. + ** Return: TRUE if Word contains no dangerous ambiguities. + ** Exceptions: none + ** History: Mon May 6 16:28:56 1991, DSJ, Created. + */ + + char NewWord[MAX_WERD_SIZE]; + char *NextNewChar; + int bad_index = 0; + + if (!AmbigFor) + AmbigFor = FillAmbigTable (); + + NextNewChar = NewWord; + while (*Word) + if (AmbigsFound (NewWord, NextNewChar, Word + 1, AmbigFor[*Word], fixpt)) { + if (fixpt != NULL) + fixpt->index = bad_index; + return (FALSE); + } + else { + *NextNewChar++ = *Word++; + bad_index++; + } + + return (TRUE); + +} /* NoDangerousAmbig */ + +void EndDangerousAmbigs() { + if (AmbigFor != NULL) { + for (int i = 0; i <= MAX_CLASS_ID; ++i) { + destroy_nodes(AmbigFor[i], Efree); + } + Efree(AmbigFor); + AmbigFor = NULL; + } +} + +/*---------------------------------------------------------------------------*/ +void SettupStopperPass1() { +/* + ** Parameters: none + ** Globals: + ** RejectOffset offset allowed before word is rejected + ** Operation: This routine performs any settup of stopper variables + ** that is needed in preparation for the first pass. + ** Return: none + ** Exceptions: none + ** History: Mon Jun 3 12:32:00 1991, DSJ, Created. + */ + RejectOffset = 0.0; +} /* SettupStopperPass1 */ + + +/*---------------------------------------------------------------------------*/ +void SettupStopperPass2() { +/* + ** Parameters: none + ** Globals: + ** RejectOffset offset allowed before word is rejected + ** Operation: This routine performs any settup of stopper variables + ** that is needed in preparation for the second pass. + ** Return: none + ** Exceptions: none + ** History: Mon Jun 3 12:32:00 1991, DSJ, Created. + */ + RejectOffset = RejectCertaintyOffset; +} /* SettupStopperPass2 */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void AddNewChunk(VIABLE_CHOICE Choice, int Blob) { +/* + ** Parameters: + ** Choice choice to add a new chunk to + ** Blob index of blob being split + ** Globals: none + ** Operation: This routine increments the chunk count of the character + ** in Choice which corresponds to Blob. + ** Return: none + ** Exceptions: none + ** History: Mon May 20 11:43:27 1991, DSJ, Created. + */ + int i, LastChunk; + + for (i = 0, LastChunk = 0; i < Choice->Length; i++) { + LastChunk += Choice->Blob[i].NumChunks; + if (Blob < LastChunk) { + (Choice->Blob[i].NumChunks)++; + return; + } + } + mem_tidy (1); + cprintf ("AddNewChunk failed:Choice->Length=%d, LastChunk=%d, Blob=%d\n", + Choice->Length, LastChunk, Blob); + assert(FALSE); /* this should never get executed */ + +} /* AddNewChunk */ + + +/*---------------------------------------------------------------------------*/ +int AmbigsFound(char *Word, + char *CurrentChar, + const char *Tail, + LIST Ambigs, + DANGERR *fixpt) { +/* + ** Parameters: + ** Word word being tested for ambiguities + ** CurrentChar position in Word to put ambig replacement + ** Tail end of word to place after ambiguity + ** Ambigs list of ambiguities to test at this position + ** Globals: none + ** Operation: For each ambiguity in Ambigs, see if the remainder of + ** the test string matches the start of Tail. If it does, + ** construct a word consisting of the contents of Word up to, + ** but not including, CurrentChar followed by the replacement + ** string for the ambiguity followed by the unmatched + ** contents of Tail. Then test this word to see if it + ** is a dictionary word. If it is return TRUE. If none of + ** the ambiguities result in a dictionary word, return FALSE. + ** Return: TRUE if the Word is ambiguous at the specified position + ** Exceptions: none + ** History: Thu May 9 10:10:28 1991, DSJ, Created. + */ + char *AmbigSpec; + const char *UnmatchedTail; + int Matches; + int bad_length; + + iterate(Ambigs) { + AmbigSpec = (char *) first (Ambigs); + bad_length = 1; + UnmatchedTail = Tail; + Matches = TRUE; + + while (*AmbigSpec != ' ' && Matches) + if (*AmbigSpec == *UnmatchedTail) { + AmbigSpec++; + UnmatchedTail++; + bad_length++; + } + else + Matches = FALSE; + + if (Matches) { + AmbigSpec++; /* skip over the space */ + /* insert replacement string */ + strcpy(CurrentChar, AmbigSpec); + /* add tail */ + strcat(Word, UnmatchedTail); + if (valid_word (Word)) { + if (StopperDebugLevel >= 1) + cprintf ("Stopper: Possible ambiguous word = %s\n", Word); + if (fixpt != NULL) { + fixpt->good_length = strlen (AmbigSpec); + fixpt->bad_length = bad_length; + } + return (TRUE); + } + } + } + return (FALSE); + +} /* AmbigsFound */ + + +/*---------------------------------------------------------------------------*/ +int ChoiceSameAs(A_CHOICE *Choice, VIABLE_CHOICE ViableChoice) { +/* + ** Parameters: + ** Choice choice to compare to ViableChoice + ** ViableChoice viable choice to compare to Choice + ** Globals: none + ** Operation: This routine compares the corresponding strings of + ** Choice and ViableChoice and returns TRUE if they are the + ** same, FALSE otherwise. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Fri May 17 08:48:04 1991, DSJ, Created. + */ + return (StringSameAs (class_string (Choice), ViableChoice)); + +} /* ChoiceSameAs */ + + +/*---------------------------------------------------------------------------*/ +int CmpChoiceRatings(void *arg1, //VIABLE_CHOICE Choice1, + void *arg2) { //VIABLE_CHOICE Choice2) +/* + ** Parameters: + ** Choice1, Choice2 choices to compare ratings for + ** Globals: none + ** Operation: Return -1 if the rating for Choice1 is less than the + ** rating for Choice2, otherwise return (1). + ** Return: -1 or 1 + ** Exceptions: none + ** History: Wed May 15 13:02:37 1991, DSJ, Created. + */ + float R1, R2; + VIABLE_CHOICE Choice1 = (VIABLE_CHOICE) arg1; + VIABLE_CHOICE Choice2 = (VIABLE_CHOICE) arg2; + + R1 = Choice1->Rating; + R2 = Choice2->Rating; + + if (R1 < R2) + return (-1); + else + return (1); + +} /* CmpChoiceRatings */ + + +/*---------------------------------------------------------------------------*/ +void ExpandChoice(VIABLE_CHOICE Choice, EXPANDED_CHOICE *ExpandedChoice) { +/* + ** Parameters: + ** Choice choice to be expanded + ** ExpandedChoice place to put resulting expanded choice + ** Globals: none + ** Operation: This routine expands Choice and places the results + ** in ExpandedChoice. The primary function of expansion + ** is to create an two arrays, one which holds the corresponding + ** certainty for each chunk in Choice, and one which holds + ** the class for each chunk. + ** Return: none (results are placed in ExpandedChoice) + ** Exceptions: none + ** History: Fri May 31 15:21:57 1991, DSJ, Created. + */ + int i, j, Chunk; + + ExpandedChoice->Choice = Choice; + for (i = 0, Chunk = 0; i < Choice->Length; i++) + for (j = 0; j < Choice->Blob[i].NumChunks; j++, Chunk++) { + ExpandedChoice->ChunkCertainty[Chunk] = Choice->Blob[i].Certainty; + ExpandedChoice->ChunkClass[Chunk] = Choice->Blob[i].Class; + } +} /* ExpandChoice */ + + +/*---------------------------------------------------------------------------*/ +AMBIG_TABLE *FillAmbigTable() { +/* + ** Parameters: none + ** Globals: + ** DangerousAmbigs filename of dangerous ambig info + ** Operation: This routine allocates a new ambiguity table and fills + ** it in from the file specified by DangerousAmbigs. An + ** ambiguity table is an array of lists. The array is indexed + ** by a class id. Therefore, each entry in the table provides + ** a list of potential ambiguities which can start with the + ** corresponding character. Each potential ambiguity is + ** described by a string which contains the remainder of the + ** test string followed by a space followed by the replacement + ** string. For example the ambiguity "rn -> m", would be + ** located in the table at index 'r'. The string corresponding + ** to this ambiguity would be "n m". + ** Return: Pointer to new ambiguity table. + ** Exceptions: none + ** History: Thu May 9 09:20:57 1991, DSJ, Created. + */ + FILE *AmbigFile; + AMBIG_TABLE *NewTable; + int i; + char TestString[256]; + char ReplacementString[256]; + char name[1024]; + char *AmbigSpec; + int AmbigSize; + + strcpy(name, demodir); + strcat(name, DangerousAmbigs); + AmbigFile = Efopen (name, "r"); + NewTable = (AMBIG_TABLE *) Emalloc (sizeof (LIST) * (MAX_CLASS_ID + 1)); + + for (i = 0; i <= MAX_CLASS_ID; i++) + NewTable[i] = NIL; + + while (fscanf (AmbigFile, "%s", TestString) == 1 && + fscanf (AmbigFile, "%s", ReplacementString) == 1) { + if (strlen (TestString) > MAX_AMBIG_SIZE || + strlen (ReplacementString) > MAX_AMBIG_SIZE) + DoError (0, "Illegal ambiguity specification!"); + + AmbigSize = strlen (TestString) + strlen (ReplacementString) + 1; + AmbigSpec = (char *) Emalloc (sizeof (char) * AmbigSize); + + strcpy (AmbigSpec, &(TestString[1])); + strcat (AmbigSpec, " "); + strcat(AmbigSpec, ReplacementString); + NewTable[TestString[0]] = + push_last (NewTable[TestString[0]], AmbigSpec); + } + fclose(AmbigFile); + return (NewTable); + +} /* FillAmbigTable */ + + +/*---------------------------------------------------------------------------*/ +int FreeBadChoice(void *item1, //VIABLE_CHOICE Choice, + void *item2) { //EXPANDED_CHOICE *BestChoice) +/* + ** Parameters: + ** Choice choice to be tested + ** BestChoice best choice found + ** Globals: + ** AmbigThresholdGain + ** AmbigThresholdOffset + ** Operation: If the certainty of any chunk in Choice is not ambiguous + ** with the corresponding chunk in the best choice, free + ** Choice and return TRUE. Otherwise, return FALSE. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Wed May 15 13:20:26 1991, DSJ, Created. + */ + int i, j, Chunk; + FLOAT32 Threshold; + VIABLE_CHOICE Choice; + EXPANDED_CHOICE *BestChoice; + + Choice = (VIABLE_CHOICE) item1; + BestChoice = (EXPANDED_CHOICE *) item2; + + Threshold = AmbigThreshold (BestChoice->Choice->AdjustFactor, + Choice->AdjustFactor); + + for (i = 0, Chunk = 0; i < Choice->Length; i++) + for (j = 0; j < Choice->Blob[i].NumChunks; j++, Chunk++) + if (Choice->Blob[i].Class != BestChoice->ChunkClass[Chunk] && + Choice->Blob[i].Certainty - BestChoice->ChunkCertainty[Chunk] < + Threshold) { + memfree(Choice); + return (TRUE); + } + + return (FALSE); + +} /* FreeBadChoice */ + + +/*---------------------------------------------------------------------------*/ +int LengthOfShortestAlphaRun(register char *Word) { +/* + ** Parameters: + ** Word word to be tested + ** Globals: none + ** Operation: Return the length of the shortest alpha run in Word. + ** Return: Return the length of the shortest alpha run in Word. + ** Exceptions: none + ** History: Tue May 14 07:50:45 1991, DSJ, Created. + */ + register int Shortest = MAXINT; + register int Length; + + for (; *Word; Word++) + if (isalpha (*Word)) { + for (Length = 1, Word++; isalpha (*Word); Word++, Length++); + if (Length < Shortest) + Shortest = Length; + + if (*Word == 0) + break; + } + if (Shortest == MAXINT) + Shortest = 0; + + return (Shortest); + +} /* LengthOfShortestAlphaRun */ + + +/*---------------------------------------------------------------------------*/ +VIABLE_CHOICE +NewViableChoice (A_CHOICE * Choice, FLOAT32 AdjustFactor, float Certainties[]) { +/* + ** Parameters: + ** Choice choice to be converted to a viable choice + ** AdjustFactor factor used to adjust ratings for Choice + ** Certainties certainty for each character in Choice + ** Globals: + ** CurrentSegmentation segmentation corresponding to Choice + ** Operation: Allocate a new viable choice data structure, copy + ** Choice, Certainties, and CurrentSegmentation into it, + ** and return a pointer to it. + ** Return: Ptr to new viable choice. + ** Exceptions: none + ** History: Thu May 16 15:28:29 1991, DSJ, Created. + */ + VIABLE_CHOICE NewChoice; + int Length; + char *Word; + CHAR_CHOICE *NewChar; + BLOB_WIDTH *BlobWidth; + + Length = strlen (class_string (Choice)); + assert (Length <= MAX_NUM_CHUNKS && Length > 0); + + NewChoice = (VIABLE_CHOICE) Emalloc (sizeof (VIABLE_CHOICE_STRUCT) + + (Length - 1) * sizeof (CHAR_CHOICE)); + + NewChoice->Rating = class_probability (Choice); + NewChoice->Certainty = class_certainty (Choice); + NewChoice->AdjustFactor = AdjustFactor; + NewChoice->Length = Length; + + for (Word = class_string (Choice), + NewChar = &(NewChoice->Blob[0]), + BlobWidth = CurrentSegmentation; + *Word; Word++, NewChar++, Certainties++, BlobWidth++) { + NewChar->Class = *Word; + NewChar->NumChunks = *BlobWidth; + NewChar->Certainty = *Certainties; + } + + return (NewChoice); + +} /* NewViableChoice */ + + +/*---------------------------------------------------------------------------*/ +void PrintViableChoice(FILE *File, const char *Label, VIABLE_CHOICE Choice) { +/* + ** Parameters: + ** File open text file to print Choice to + ** Label text label to be printed with Choice + ** Choice choice to be printed + ** Globals: none + ** Operation: This routine dumps a text representation of the + ** specified Choice to File. + ** Return: none + ** Exceptions: none + ** History: Mon May 20 11:16:44 1991, DSJ, Created. + */ + int i, j; + + fprintf (File, "%s", Label); + + fprintf (File, "(R=%5.1f, C=%4.1f, F=%4.2f) ", + Choice->Rating, Choice->Certainty, Choice->AdjustFactor); + + for (i = 0; i < Choice->Length; i++) + fprintf (File, "%c", Choice->Blob[i].Class); + fprintf (File, "\n"); + + for (i = 0; i < Choice->Length; i++) { + fprintf (File, " %c", Choice->Blob[i].Class); + for (j = 0; j < Choice->Blob[i].NumChunks - 1; j++) + fprintf (File, " "); + } + fprintf (File, "\n"); + + for (i = 0; i < Choice->Length; i++) { + for (j = 0; j < Choice->Blob[i].NumChunks; j++) + fprintf (File, "%3d", (int) (Choice->Blob[i].Certainty * -10.0)); + } + fprintf (File, "\n"); + +} /* PrintViableChoice */ + + +/*---------------------------------------------------------------------------*/ +void +ReplaceDuplicateChoice (VIABLE_CHOICE OldChoice, +A_CHOICE * NewChoice, +FLOAT32 AdjustFactor, float Certainties[]) { +/* + ** Parameters: + ** OldChoice existing viable choice to be replaced + ** NewChoice choice to replace OldChoice with + ** AdjustFactor factor used to adjust ratings for OldChoice + ** Certainties certainty for each character in OldChoice + ** Globals: + ** CurrentSegmentation segmentation for NewChoice + ** Operation: This routine is used whenever a better segmentation (or + ** contextual interpretation) is found for a word which already + ** exists. The OldChoice is updated with the relevant + ** information from the new choice. The text string itself + ** does not need to be copied since, by definition, has not + ** changed. + ** Return: none + ** Exceptions: none + ** History: Fri May 17 13:35:58 1991, DSJ, Created. + */ + char *Word; + CHAR_CHOICE *NewChar; + BLOB_WIDTH *BlobWidth; + + OldChoice->Rating = class_probability (NewChoice); + OldChoice->Certainty = class_certainty (NewChoice); + OldChoice->AdjustFactor = AdjustFactor; + + for (Word = class_string (NewChoice), + NewChar = &(OldChoice->Blob[0]), + BlobWidth = CurrentSegmentation; + *Word; Word++, NewChar++, Certainties++, BlobWidth++) { + NewChar->NumChunks = *BlobWidth; + NewChar->Certainty = *Certainties; + } +} /* ReplaceDuplicateChoice */ + + +/*---------------------------------------------------------------------------*/ +int StringSameAs(const char *String, VIABLE_CHOICE ViableChoice) { +/* + ** Parameters: + ** String string to compare to ViableChoice + ** ViableChoice viable choice to compare to String + ** Globals: none + ** Operation: This routine compares String to ViableChoice and + ** returns TRUE if they are the same, FALSE otherwise. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Fri May 17 08:48:04 1991, DSJ, Created. + */ + CHAR_CHOICE *Char; + int i; + + for (Char = &(ViableChoice->Blob[0]), i = 0; + i < ViableChoice->Length; String++, Char++, i++) + if (*String != Char->Class) + return (FALSE); + + if (*String == 0) + return (TRUE); + else + return (FALSE); + +} /* StringSameAs */ + + +/*---------------------------------------------------------------------------*/ +int UniformCertainties(CHOICES_LIST Choices, A_CHOICE *BestChoice) { +/* + ** Parameters: + ** Choices choices for current segmentation + ** BestChoice best choice for current segmentation + ** Globals: + ** CertaintyVariation max allowed certainty variation + ** Operation: This routine returns TRUE if the certainty of the + ** BestChoice word is within a reasonable range of the average + ** certainties for the best choices for each character in + ** the segmentation. This test is used to catch words in which + ** one character is much worse than the other characters in + ** the word (i.e. FALSE will be returned in that case). + ** The algorithm computes the mean and std deviation of the + ** certainties in the word with the worst certainty thrown out. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Tue May 14 08:23:21 1991, DSJ, Created. + */ + int i; + CHOICES CharChoices; + float Certainty; + float WorstCertainty = MAX_FLOAT32; + float CertaintyThreshold; + FLOAT64 TotalCertainty; + FLOAT64 TotalCertaintySquared; + FLOAT64 Variance; + FLOAT32 Mean, StdDev; + int WordLength; + + WordLength = array_count (Choices); + if (WordLength < 3) + return (TRUE); + + TotalCertainty = TotalCertaintySquared = 0.0; + for_each_choice(Choices, i) { + CharChoices = (CHOICES) array_index (Choices, i); + Certainty = best_certainty (CharChoices); + TotalCertainty += Certainty; + TotalCertaintySquared += Certainty * Certainty; + if (Certainty < WorstCertainty) + WorstCertainty = Certainty; + } + + /* subtract off worst certainty from statistics */ + WordLength--; + TotalCertainty -= WorstCertainty; + TotalCertaintySquared -= WorstCertainty * WorstCertainty; + + Mean = TotalCertainty / WordLength; + Variance = ((WordLength * TotalCertaintySquared - + TotalCertainty * TotalCertainty) / + (WordLength * (WordLength - 1))); + if (Variance < 0.0) + Variance = 0.0; + StdDev = sqrt (Variance); + + CertaintyThreshold = Mean - CertaintyVariation * StdDev; + if (CertaintyThreshold > NonDictCertainty) + CertaintyThreshold = NonDictCertainty; + + if (class_certainty (BestChoice) < CertaintyThreshold) { + if (StopperDebugLevel >= 1) + cprintf + ("Stopper: Non-uniform certainty = %4.1f (m=%4.1f, s=%4.1f, t=%4.1f)\n", + class_certainty (BestChoice), Mean, StdDev, CertaintyThreshold); + return (FALSE); + } + else + return (TRUE); + +} /* UniformCertainties */ diff --git a/dict/stopper.h b/dict/stopper.h new file mode 100644 index 0000000000..5101c378d9 --- /dev/null +++ b/dict/stopper.h @@ -0,0 +1,101 @@ +/****************************************************************************** + ** Filename: stopper.h + ** Purpose: Stopping criteria for word classifier. + ** Author: Dan Johnson + ** History: Wed May 1 09:42:57 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef STOPPER_H +#define STOPPER_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "choicearr.h" +#include "states.h" + +typedef UINT8 BLOB_WIDTH; + +typedef struct +{ + INT16 index; + unsigned bad_length:8; + unsigned good_length:8; +} DANGERR; + +/*--------------------------------------------------------------------------- + Variables +---------------------------------------------------------------------------*/ +extern float CertaintyPerChar; +extern float NonDictCertainty; +extern float RejectCertaintyOffset; +extern int StopperDebugLevel; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +#define DisableChoiceAccum() (KeepWordChoices = FALSE) +#define EnableChoiceAccum() (KeepWordChoices = TRUE) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +int AcceptableChoice(CHOICES_LIST Choices, + A_CHOICE *BestChoice, + A_CHOICE *RawChoice, + DANGERR *fixpt); + +int AcceptableResult(A_CHOICE *BestChoice, A_CHOICE *RawChoice); + +int AlternativeChoicesWorseThan(FLOAT32 Threshold); + +int CurrentBestChoiceIs(const char *Word); + +FLOAT32 CurrentBestChoiceAdjustFactor(); + +int CurrentWordAmbig(); + +void DebugWordChoices(); + +void FilterWordChoices(); + +void FindClassifierErrors (FLOAT32 MinRating, +FLOAT32 MaxRating, +FLOAT32 RatingMargin, FLOAT32 Thresholds[]); + +void InitStopperVars(); + +void InitChoiceAccum(); + +void LogNewRawChoice (A_CHOICE * Choice, +FLOAT32 AdjustFactor, float Certainties[]); + +void LogNewSegmentation(PIECES_STATE BlobWidth); + +void LogNewSplit(int Blob); + +void LogNewWordChoice (A_CHOICE * Choice, +FLOAT32 AdjustFactor, float Certainties[]); + +int NoDangerousAmbig(const char *Word, DANGERR *fixpt); +void EndDangerousAmbigs(); + +void SettupStopperPass1(); + +void SettupStopperPass2(); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern BOOL8 KeepWordChoices; +#endif diff --git a/dict/trie.cpp b/dict/trie.cpp new file mode 100644 index 0000000000..6dfea67cee --- /dev/null +++ b/dict/trie.cpp @@ -0,0 +1,463 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: trie.c (Formerly trie.c) + * Description: Functions to build a trie data structure. + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Fri Jul 26 12:18:10 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "trie.h" +#include "callcpp.h" + +#ifdef __UNIX__ +#include +#endif +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static INT32 move_counter = 0; +static INT32 new_counter = 0; +static INT32 edge_counter = 0; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * add_edge_linkage + * + * Add a single edge linkage to between the two nodes. This function + * can be used to add either forward or backward links. + **********************************************************************/ +void add_edge_linkage(EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + INT32 direction, + char character, + INT32 word_end) { + EDGE_REF edge1 = node1; + EDGE_REF edge2; + INT32 num_edges = edges_in_node (dawg, node1); + INT32 last_one; + + word_end = (word_end ? WERD_END_FLAG : 0); + + if (num_edges == 0) { /* No edges yet */ + direction = ((direction == FORWARD_EDGE) ? DIRECTION_FLAG : 0); + link_edge (dawg, edge1, node2, character, + LAST_FLAG | direction | word_end); + } + else { + /* Forward links */ + if (direction == FORWARD_EDGE) { + last_one = (forward_edge (dawg, edge1) ? 0 : LAST_FLAG); + if (debug) + cprintf ("moving edges (nodes = %ld, %dl, num = %ld)\n", + edge1, edge1+1, num_edges); + copy_edges (dawg, edge1, edge1+1, num_edges); + link_edge (dawg, edge1, node2, character, + last_one | DIRECTION_FLAG | word_end); + } + else { /* Backward links */ + + if (forward_edge (dawg, edge1)) + edge_loop(dawg, edge1); + + /* Existing back edges */ + if (backward_edge (dawg,edge1)) { + num_edges = 0; + edge2 = edge1; + do { num_edges++; } + edge_loop(dawg, edge2); + + if (debug) + cprintf ("moving edges (nodes = %ld, %ld, num = %ld)\n", + edge1, edge1+1, num_edges); + copy_edges (dawg, edge1, edge1+1, num_edges); + link_edge(dawg, edge1, node2, character, word_end); + } + else { /* First back edge */ + link_edge (dawg, edge1, node2, character, + LAST_FLAG | word_end); + } + } + } +} + + +/********************************************************************** + * add_new_edge + * + * Add an edge between two nodes in the DAWG. Link the nodes both ways. + **********************************************************************/ +void add_new_edge(EDGE_ARRAY dawg, + NODE_REF *node1, + NODE_REF *node2, + char character, + INT32 word_end, + INT32 max_num_edges, + INT32 reserved_edges) { + int direction; + + if (debug) + cprintf ("add_new_edge (nodes = %ld, %ld, ch = '%c', end = %d)\n", + *node1, *node2, character, word_end); + edge_counter++; + + move_node_if_needed(dawg, *node1, max_num_edges, reserved_edges); + move_node_if_needed(dawg, *node2, max_num_edges, reserved_edges); + + direction = (int) FORWARD_EDGE; + + add_edge_linkage(dawg, *node1, *node2, direction, character, word_end); + + direction = (int) BACKWARD_EDGE; + add_edge_linkage(dawg, *node2, *node1, direction, character, word_end); +} + + +/********************************************************************** + * add_word_to_dawg + * + * Add in a word by creating the necessary nodes and edges. + **********************************************************************/ +void add_word_to_dawg(EDGE_ARRAY dawg, + char *string, + INT32 max_num_edges, + INT32 reserved_edges) { + EDGE_REF edge; + NODE_REF last_node = 0; + NODE_REF the_next_node; + INT32 i; + INT32 still_finding_chars = TRUE; + INT32 word_end = FALSE; + + for (i=0; i reserved_edges) { + cprintf ("error: Not enough room in root node, %d\n", + edges_in_node (dawg, 0)); + exit (1); + } +} + + +/********************************************************************** + * initialize_dawg + * + * Initialize the DAWG data structure for further used. Reset each of + * the edge cells to NO_EDGE. + **********************************************************************/ +void initialize_dawg(EDGE_ARRAY dawg, INT32 max_num_edges) { + INT32 x; + + clear_all_edges(dawg, x, max_num_edges); +} + + +/********************************************************************** + * move_node + * + * Move the location in the edge array of this node in the DAWG. + **********************************************************************/ +NODE_REF move_node(EDGE_ARRAY dawg, + NODE_REF node, + INT32 max_num_edges, + INT32 reserved_edges) { + NODE_REF this_new_node; + EDGE_REF edge; + INT32 num_edges = edges_in_node (dawg, node); + + if (debug) + print_dawg_node(dawg, node); + + this_new_node = new_dawg_node (dawg, num_edges + EDGE_NUM_MARGIN, max_num_edges, reserved_edges); + + if (debug) + cprintf ("move_node (from = %ld, to = %ld, num = %ld)\n", + node, this_new_node, num_edges); + + move_edges(dawg, node, this_new_node, num_edges); + + if (debug) + print_dawg_node(dawg, this_new_node); + + edge = this_new_node; + if (forward_edge (dawg, edge)) { + do { + relocate_edge (dawg, next_node (dawg, edge), node, this_new_node); + } edge_loop (dawg, edge); + } + if (backward_edge (dawg, edge)) { + do { + relocate_edge (dawg, next_node (dawg, edge), node, this_new_node); + } edge_loop (dawg, edge); + } + + move_counter++; + return (this_new_node); +} + + +/********************************************************************** + * new_dawg_node + * + * Create a space within the DAWG data structure to house a node that + * consists of the requested number of edges. + **********************************************************************/ +NODE_REF new_dawg_node(EDGE_ARRAY dawg, + INT32 num_edges, + INT32 max_num_edges, + INT32 reserved_edges) { + INT32 i; + INT32 n; + INT32 edge_index; + INT32 edge_collision; + /* Try several times */ + for (i=0; i %ld)\n", node, old_node, new_node); + + edge = node; + if (forward_edge (dawg, edge)) { + do { + if (next_node (dawg, edge) == old_node) { + if (debug) + cprintf ("forward assign (%ld, %ld ==> %ld)\n", edge, old_node, new_node); + + set_next_edge(dawg, edge, new_node); + } + } edge_loop (dawg, edge); + } + + if (backward_edge (dawg, edge)) { + do { + if (next_node (dawg, edge) == old_node) { + if (debug) + cprintf ("backward assign (%ld, %ld ==> %ld)\n", edge, old_node, new_node); + + set_next_edge(dawg, edge, new_node); + } + } + edge_loop(dawg, edge); + } +} + + +/********************************************************************** + * remove_edge + * + * Add a single edge linkage to between the two nodes. This function + * can be used to add either forward or backward links. + **********************************************************************/ +void remove_edge(EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + char character, + INT32 word_end) { + remove_edge_linkage(dawg, node1, node2, FORWARD_EDGE, character, word_end); + + remove_edge_linkage(dawg, node2, node1, BACKWARD_EDGE, character, word_end); +} + + +/********************************************************************** + * remove_edge_linkage + * + * Remove this edge reference from this node. Move the edge entries in + * this node to fill the gap. + **********************************************************************/ +void remove_edge_linkage(EDGE_ARRAY dawg, + NODE_REF node, + NODE_REF next, + INT32 direction, + char character, + INT32 word_end) { + INT32 forward_edges; + INT32 num_edges; + INT32 e = node; + INT32 last_flag; + + forward_edges = num_forward_edges (dawg, node); + num_edges = edges_in_node (dawg, node); + + for (e=node; e=0; i--) { \ + copy_edge(dawg,from+i,to+i); \ + } \ +} \ + + +/********************************************************************** + * move_node_if_needed + * + * Move the node location if there is a need to do it because there is + * not enough room to add the new edges. + **********************************************************************/ + +#define move_node_if_needed(dawg,node,max_num_edges,reserved_edges) \ +if (! room_in_node (dawg, node)) { \ + node = move_node (dawg, node, max_num_edges, reserved_edges); \ +} \ + + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void add_edge_linkage(EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + INT32 direction, + char character, + INT32 word_end); + +void add_new_edge(EDGE_ARRAY dawg, + NODE_REF *node1, + NODE_REF *node2, + char character, + INT32 word_end, + INT32 max_num_edges, + INT32 reserved_edges); + +void add_word_to_dawg(EDGE_ARRAY dawg, + char *string, + INT32 max_num_edges, + INT32 reserved_edges); + +void initialize_dawg(EDGE_ARRAY dawg, INT32 max_num_edges); + +NODE_REF move_node(EDGE_ARRAY dawg, + NODE_REF node, + INT32 max_num_edges, + INT32 reserved_edges); + +NODE_REF new_dawg_node(EDGE_ARRAY dawg, + INT32 num_edges, + INT32 max_num_edges, + INT32 reserved_edges); + +void read_word_list(char *filename, + EDGE_ARRAY dawg, + INT32 max_num_edges, + INT32 reserved_edges); + +void relocate_edge(EDGE_ARRAY dawg, + NODE_REF node, + NODE_REF old_node, + NODE_REF new_node); + +void remove_edge(EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + char character, + INT32 word_end); + +void remove_edge_linkage(EDGE_ARRAY dawg, + NODE_REF node, + NODE_REF next, + INT32 direction, + char character, + INT32 word_end); + +INT32 room_in_node(EDGE_ARRAY dawg, NODE_REF node); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* trie.c * +void add_edge_linkage + _ARGS((EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + INT32 direction, + int character, + INT32 word_end)); + +void add_new_edge + _ARGS((EDGE_ARRAY dawg, + NODE_REF *node1, + NODE_REF *node2, + int character, + INT32 word_end, + INT32 max_num_edges, + INT32 reserved_edges)); + +void add_word_to_dawg + _ARGS((EDGE_ARRAY dawg, + char *string, + INT32 max_num_edges, + INT32 reserved_edges)); + +void initialize_dawg + _ARGS((EDGE_ARRAY dawg, + INT32 max_num_edges)); + +NODE_REF move_node + _ARGS((EDGE_ARRAY dawg, + NODE_REF node, + INT32 max_num_edges, + INT32 reserved_edges)); + +NODE_REF new_dawg_node + _ARGS((EDGE_ARRAY dawg, + INT32 num_edges, + INT32 max_num_edges, + INT32 reserved_edges)); + +void read_word_list + _ARGS((char *filename, + EDGE_ARRAY dawg, + INT32 max_num_edges, + INT32 reserved_edges)); + +void relocate_edge + _ARGS((EDGE_ARRAY dawg, + NODE_REF node, + NODE_REF old_node, + NODE_REF new_node)); + +void remove_edge + _ARGS((EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + int character, + INT32 word_end)); + +void remove_edge_linkage + _ARGS((EDGE_ARRAY dawg, + NODE_REF node, + NODE_REF next, + INT32 direction, + int character, + INT32 word_end)); + +INT32 room_in_node + _ARGS((EDGE_ARRAY dawg, + NODE_REF node)); + +#undef _ARGS +*/ +#endif diff --git a/display/Makefile.am b/display/Makefile.am new file mode 100644 index 0000000000..b9a8e3a744 --- /dev/null +++ b/display/Makefile.am @@ -0,0 +1,14 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/image -I$(top_srcdir)/ccstruct \ + -I$(top_srcdir)/viewer -I$(top_srcdir)/textord \ + -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + cmndwin.h pagewalk.h pgedit.h pgeditx.h sbdmenu.h submen.h \ + tessio.h varabled.h varblmen.h varblwin.h + +noinst_LIBRARIES = libtesseract_display.a +libtesseract_display_a_SOURCES = \ + cmndwin.cpp pagewalk.cpp pgedit.cpp sbdmenu.cpp varabled.cpp \ + varblmen.cpp varblwin.cpp diff --git a/display/Makefile.in b/display/Makefile.in new file mode 100644 index 0000000000..90e8144c1c --- /dev/null +++ b/display/Makefile.in @@ -0,0 +1,539 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = display +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_display_a_AR = $(AR) $(ARFLAGS) +libtesseract_display_a_LIBADD = +am_libtesseract_display_a_OBJECTS = cmndwin.$(OBJEXT) \ + pagewalk.$(OBJEXT) pgedit.$(OBJEXT) sbdmenu.$(OBJEXT) \ + varabled.$(OBJEXT) varblmen.$(OBJEXT) varblwin.$(OBJEXT) +libtesseract_display_a_OBJECTS = $(am_libtesseract_display_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_display_a_SOURCES) +DIST_SOURCES = $(libtesseract_display_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/image -I$(top_srcdir)/ccstruct \ + -I$(top_srcdir)/viewer -I$(top_srcdir)/textord \ + -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + cmndwin.h pagewalk.h pgedit.h pgeditx.h sbdmenu.h submen.h \ + tessio.h varabled.h varblmen.h varblwin.h + +noinst_LIBRARIES = libtesseract_display.a +libtesseract_display_a_SOURCES = \ + cmndwin.cpp pagewalk.cpp pgedit.cpp sbdmenu.cpp varabled.cpp \ + varblmen.cpp varblwin.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu display/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu display/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_display.a: $(libtesseract_display_a_OBJECTS) $(libtesseract_display_a_DEPENDENCIES) + -rm -f libtesseract_display.a + $(libtesseract_display_a_AR) libtesseract_display.a $(libtesseract_display_a_OBJECTS) $(libtesseract_display_a_LIBADD) + $(RANLIB) libtesseract_display.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmndwin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pagewalk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pgedit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sbdmenu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/varabled.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/varblmen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/varblwin.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/display/cmndwin.cpp b/display/cmndwin.cpp new file mode 100644 index 0000000000..9ca62f5391 --- /dev/null +++ b/display/cmndwin.cpp @@ -0,0 +1,449 @@ +/********************************************************************** + * File: cmndwin.cpp (Formerly cmdwin.c) + * Description: Command Window class + * Author: Phil Cheatle + * Created: Thu Oct 10 16:25:24 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include "evnts.h" +#include "cmndwin.h" + +#define BACKSPACE_KEY '\010' +#define RETURN_KEY '\015' + +#define BORDER_HEIGHT (menu_char_height / 2) + +#define MSG_AREA_TOP_LEFT_X 0 +#define MSG_AREA_TOP_LEFT_Y 0 +#define MSG_AREA_HEIGHT (int)(menu_char_height * 1.5 ) +#define MSG_TEXT_START_X (menu_char_height / 2) +#define MSG_TEXT_START_Y (MSG_AREA_TOP_LEFT_Y - menu_char_height) + +#define PROMPT_AREA_TOP_LEFT_X 0 +#define PROMPT_AREA_TOP_LEFT_Y \ + (MSG_AREA_TOP_LEFT_Y - MSG_AREA_HEIGHT - BORDER_HEIGHT) +#define PROMPT_AREA_HEIGHT MSG_AREA_HEIGHT +#define PROMPT_TEXT_START_X (menu_char_height / 2) +#define PROMPT_TEXT_START_Y (PROMPT_AREA_TOP_LEFT_Y - menu_char_height) + +#define MENU_AREA_TOP_LEFT_X 0 +#define MENU_AREA_TOP_LEFT_Y \ + (PROMPT_AREA_TOP_LEFT_Y - PROMPT_AREA_HEIGHT - BORDER_HEIGHT) + +#define MIN_WINDOW_WIDTH (PROMPT_TEXT_START_X + 80 * menu_char_width) + +#define EXTERN + +EXTERN INT_VAR (editor_cmdwin_width, 950, "CmdWin max non scrollable width"); +EXTERN INT_VAR (editor_cmdwin_height, 550, +"CmdWin max non scrollable height"); +EXTERN INT_VAR (editor_cmdwin_xpos1, 20, "X pos of first command window"); +EXTERN INT_VAR (editor_cmdwin_ypos1, 20, "Y pos of first command window"); +EXTERN INT_VAR (editor_cmdwin_xoffset, 30, "X offset between command windws"); +EXTERN INT_VAR (editor_cmdwin_yoffset, 30, "Y offset between command windws"); + +INT16 +COMMAND_WINDOW::next_win_x_pos = 0; +INT16 +COMMAND_WINDOW::next_win_y_pos = 0; + +/********************************************************************** + * COMMAND_WINDOW::COMMAND_WINDOW() + * + * COMMAND_WINDOW constructor + **********************************************************************/ + + // window name +COMMAND_WINDOW::COMMAND_WINDOW(const char *name, + MENU_ROOT *menu_ptr // root of menu + ) { + BOX menu_box; + INT16 window_height; + INT8 window_type = FULLSIZEWIN;//Default + INT16 xsize; + INT16 ysize; + + message_str[0] = '\0'; + prompt_str[0] = '\0'; + strcpy(win_name, name); + + menu_root = menu_ptr; + menu_box = menu_root->recalc_bounding_box (MENU_AREA_TOP_LEFT_X, + MENU_AREA_TOP_LEFT_Y); + + if (menu_box.width () > MIN_WINDOW_WIDTH) { + window_width = menu_box.width (); + } + else { + window_width = MIN_WINDOW_WIDTH; + } + + window_height = MSG_AREA_TOP_LEFT_Y - menu_box.bottom (); + + xsize = window_width; + ysize = window_height; + + if (window_width > editor_cmdwin_width) { + window_type = SCROLLINGWIN; + xsize = editor_cmdwin_width; + } + + if (window_height > editor_cmdwin_height) { + window_type = SCROLLINGWIN; + ysize = editor_cmdwin_height; + } + + if ((next_win_x_pos == 0) && (next_win_x_pos == 0)) { + //Init for 1st win + next_win_x_pos = editor_cmdwin_xpos1; + next_win_y_pos = editor_cmdwin_ypos1; + } + x_pos = next_win_x_pos; + y_pos = next_win_y_pos; + + //min x + fd = create_window (name, window_type, x_pos, y_pos, xsize, ysize, MSG_AREA_TOP_LEFT_X, + window_width, //max x + menu_box.bottom (), //min y + MSG_AREA_TOP_LEFT_Y, //max_y + TRUE, //mouse DOWN + FALSE, FALSE, TRUE); //key press + + vdc_extent (fd, 0, 0, xsize, ysize); + next_win_x_pos += editor_cmdwin_xoffset; + next_win_y_pos += editor_cmdwin_yoffset; + + plot(); +} + + +/********************************************************************** + * COMMAND_WINDOW::event() + * + * COMMAND_WINDOW event handler + **********************************************************************/ + +void COMMAND_WINDOW::event( //Process event //Command event type + GRAPHICS_EVENT &g_event, + INT32 *cmd_event, + char *new_value //of menu item + ) { + message_str[0] = '\0'; + prompt_str[0] = '\0'; + *cmd_event = UNIDENTIFIED_COMMAND; + new_value[0] = '\0'; + + switch (g_event.type) { + case DOWN_EVENT: + { + menu_root->event (this, FCOORD (g_event.x, g_event.y), + cmd_event, new_value); + if (*cmd_event == UNIDENTIFIED_COMMAND) + *cmd_event = NULL_COMMAND; + break; + } + case KEYPRESS_EVENT: + { + if (g_event.key == 3) { //Control C + exit (0); + } + } + default: + { + // sprintf( message_str, "ERROR: Unrecognised graphics event %d", + // g_event.type ); + *cmd_event = NULL_COMMAND; + break; + } + } + plot(); +} + + +/********************************************************************** + * COMMAND_WINDOW::msg() + * + * COMMAND_WINDOW message display + **********************************************************************/ + +void COMMAND_WINDOW::msg( //Display message + const char *msg_str //Text to display + ) { + strcpy(message_str, msg_str); + plot_msg_area(); + overlap_picture_ops(TRUE); +} + + +/********************************************************************** + * COMMAND_WINDOW::plot() + * + * COMMAND_WINDOW (re) paint the window + **********************************************************************/ + +void COMMAND_WINDOW::plot() { + clear_view_surface(fd); + text_font_index (fd, 1); + character_height(fd, menu_char_height); + + plot_msg_area(); + plot_prompt_area(); + + menu_root->plot (fd); +} + + +/********************************************************************** + * COMMAND_WINDOW::plot_msg_area() + * + **********************************************************************/ + +void COMMAND_WINDOW::plot_msg_area() { + fill_color_index(fd, DARK_SLATE_BLUE); + interior_style(fd, INT_SOLID, FALSE); + + rectangle (fd, + MSG_AREA_TOP_LEFT_X, MSG_AREA_TOP_LEFT_Y, + MSG_AREA_TOP_LEFT_X + window_width - 1, + MSG_AREA_TOP_LEFT_Y - MSG_AREA_HEIGHT); + + text_color_index(fd, WHITE); + text2d (fd, MSG_TEXT_START_X, MSG_TEXT_START_Y, message_str, 0, FALSE); +} + + +/********************************************************************** + * COMMAND_WINDOW::plot_prompt_area() + * + **********************************************************************/ + +void COMMAND_WINDOW::plot_prompt_area() { + INT8 i; + INT8 prompt_len; + char char_str[2]; + + fill_color_index(fd, DARK_SLATE_BLUE); + interior_style(fd, INT_SOLID, FALSE); + + rectangle (fd, + PROMPT_AREA_TOP_LEFT_X, PROMPT_AREA_TOP_LEFT_Y, + PROMPT_AREA_TOP_LEFT_X + window_width - 1, + PROMPT_AREA_TOP_LEFT_Y - MSG_AREA_HEIGHT); + + text_color_index(fd, WHITE); + prompt_len = strlen (prompt_str); + char_str[1] = '\0'; + + for (i = 0; i < prompt_len; i++) { + char_str[0] = prompt_str[i]; + text2d (fd, + PROMPT_TEXT_START_X + (i * menu_char_width), + PROMPT_TEXT_START_Y, char_str, 0, FALSE); + } +} + + +/********************************************************************** + * COMMAND_WINDOW::internal_prompt() + * + * COMMAND_WINDOW Prompt for and read a response + * WITHOUT DOING a PLOT! + **********************************************************************/ + +BOOL8 COMMAND_WINDOW::internal_prompt( //Prompt user + const char *msg_str, //Prompt message + char *response_str //Response & Default + ) { + GRAPHICS_EVENT event; + INT8 pos; + BOOL8 ok = TRUE; + + strcpy(message_str, msg_str); + strcpy(prompt_str, response_str); + plot_msg_area(); + plot_prompt_area(); + overlap_picture_ops(TRUE); + + /* MODE THE UI SO THAT IT ONLY RESPONDS TO KEYPRESSes IN THE COMMAND WINDOW */ + + event.key = '\0'; + + while (event.key != RETURN_KEY) { + await_event(fd, //just cmd window + TRUE, //wait for event + ANY_EVENT, //ONLY keypresses + &event); + + if (event.type != KEYPRESS_EVENT) { + ok = FALSE; + response_str[0] = '\0'; + break; + } + + pos = strlen (response_str); + if (isprint (event.key) && pos < MAX_CHARS) { + response_str[pos] = event.key; + response_str[pos + 1] = '\0'; + text2d (fd, + PROMPT_TEXT_START_X + (pos * menu_char_width), + PROMPT_TEXT_START_Y, response_str + pos, 0, FALSE); + } + else { + switch (event.key) { + case BACKSPACE_KEY: + if (pos > 0) { + response_str[pos - 1] = '\0'; + fill_color_index(fd, DARK_SLATE_BLUE); + interior_style(fd, INT_SOLID, FALSE); + + rectangle (fd, + PROMPT_TEXT_START_X + (pos - + 1) * menu_char_width, + PROMPT_AREA_TOP_LEFT_Y, + PROMPT_AREA_TOP_LEFT_X + window_width - 1, + PROMPT_AREA_TOP_LEFT_Y - PROMPT_AREA_HEIGHT); + } + break; + case RETURN_KEY: + break; + default: + sprintf (message_str, "NON PRINTING CHAR: %o", event.key); + plot_msg_area(); + } + } + overlap_picture_ops(TRUE); + } + message_str[0] = '\0'; + prompt_str[0] = '\0'; + #ifdef __UNIX__ + // clear_event_queue(0); //clear ALL win events + #endif + return ok; +} + + +/********************************************************************** + * COMMAND_WINDOW::press_radio_button() + * + * COMMAND_WINDOW Change the selected button in a radio set + **********************************************************************/ + +void COMMAND_WINDOW::press_radio_button( //of this radio set + RADIO_MENU *radio_sub_menu_item, //This button + RADIO_MENU_LEAF *button_menu_item) { + radio_sub_menu_item->press_radio_button (button_menu_item); + plot(); +} + + +/********************************************************************** + * COMMAND_WINDOW::update_menu_tree() + * + * Following a CHANGE to the menu tree, the + * boundng boxes must be recalculated, this may require the window to be + * re-created, the window must then be re-displayed. + **********************************************************************/ + +void COMMAND_WINDOW::update_menu_tree() { + BOX menu_box; + INT16 window_height; + INT8 window_type = FULLSIZEWIN;//Default + INT16 xsize; + INT16 ysize; + + menu_box = menu_root->recalc_bounding_box (MENU_AREA_TOP_LEFT_X, + MENU_AREA_TOP_LEFT_Y); + if (menu_box.width () > MIN_WINDOW_WIDTH) { + window_width = menu_box.width (); + } + else { + window_width = MIN_WINDOW_WIDTH; + } + + window_height = MSG_AREA_TOP_LEFT_Y - menu_box.bottom (); + + xsize = window_width; + ysize = window_height; + + if (window_width > editor_cmdwin_width) { + window_type = SCROLLINGWIN; + xsize = editor_cmdwin_width; + } + + if (window_height > editor_cmdwin_height) { + window_type = SCROLLINGWIN; + ysize = editor_cmdwin_height; + } + destroy_window(fd); + //min x + fd = create_window (win_name, window_type, x_pos, y_pos, xsize, ysize, MSG_AREA_TOP_LEFT_X, + window_width, //max x + menu_box.bottom (), //min y + MSG_AREA_TOP_LEFT_Y, //max_y + TRUE, //mouse DOWN + FALSE, FALSE, TRUE); //key press + + vdc_extent (fd, 0, 0, xsize, ysize); + plot(); +} + + +/********************************************************************** + * COMMAND_WINDOW::prompt() + * + * COMMAND_WINDOW Prompt for and read a response + **********************************************************************/ + +BOOL8 COMMAND_WINDOW::prompt( //Prompt user + const char *msg_str, //Prompt message + char *response_str //Response & Default + ) { + BOOL8 ok; + + ok = internal_prompt (msg_str, response_str); + plot(); + return ok; +} + + +/********************************************************************** + * COMMAND_WINDOW::replace_menu_text() + * + * COMMAND_WINDOW Change the label of a specified menu item + **********************************************************************/ + +void COMMAND_WINDOW::replace_menu_text( //for this item + LEAF_MENU_NODE *menu_item, + const char *new_label //New label + ) { + menu_item->new_label (new_label); + menu_item->plot (fd); +} + + +/********************************************************************** + * COMMAND_WINDOW::set_toggle() + * + * COMMAND_WINDOW Change the value of a specified menu item + **********************************************************************/ + +void COMMAND_WINDOW::set_toggle( //for this item + TOGGLE_MENU_LEAF *menu_item, + BOOL8 new_value) { + menu_item->set_toggle (new_value); + menu_item->plot (fd); +} diff --git a/display/cmndwin.h b/display/cmndwin.h new file mode 100644 index 0000000000..af66a8ccfe --- /dev/null +++ b/display/cmndwin.h @@ -0,0 +1,111 @@ +/********************************************************************** + * File: cmndwin.h (Formerly cmdwin.h) + * Description: Command Window class + * Author: Phil Cheatle + * Created: Thu Oct 10 16:25:24 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef CMNDWIN_H +#define CMNDWIN_H + +#include +#include "hosthplb.h" +#include "grphics.h" +#include "evnts.h" +#include "sbdmenu.h" +#include "notdll.h" + +#define MAX_CHARS 80 + //still decoding event +#define UNIDENTIFIED_COMMAND -1 +#define NULL_COMMAND 0 //no processing reqd + +class COMMAND_WINDOW +{ + friend class VARIABLE_MENU_LEAF; + friend class DBL_VAR_MENU_LEAF; + friend class INT_VAR_MENU_LEAF; + friend class STR_VAR_MENU_LEAF; + + INT16 window_width; //Displable width + //Message display area + char message_str[MAX_CHARS + 1]; + char prompt_str[MAX_CHARS + 1];//Prompt display area + INT16 x_pos; //Current win pos + INT16 y_pos; //Current win pos + char win_name[MAX_CHARS + 1]; //Window name + static INT16 next_win_x_pos; //Posn of next cmd win + static INT16 next_win_y_pos; //Posn of next cmd win + + protected: + WINDOW fd; //Cmd Window handle + MENU_ROOT *menu_root; //Root of menu tree + + //Prompt user(No plot) + virtual BOOL8 internal_prompt(const char *prompt_str, //Prompt message + char *response); //Response & Default + + public: + COMMAND_WINDOW( //Constructor + const char *name, //window name + MENU_ROOT *menu_ptr); + + void event( //Process event //Command event type + GRAPHICS_EVENT &g_event, + INT32 *c_event, + char *new_value); //of menu item + + void msg( //Display message + const char *message_string); //Text to display + + void plot(); //(re)paint the window + + void plot_msg_area(); + + void plot_prompt_area(); + + void press_radio_button( //Change selected item //of this radio set + RADIO_MENU *radio_sub_menu_item, //This button + RADIO_MENU_LEAF *button_menu_item); + + void update_menu_tree(); //Re calc BBoxes etc + + BOOL8 prompt( //Prompt user(& plot) + const char *prompt_str, //Prompt message + char *response); //Response & Default + + void replace_menu_text( //Change & show label + LEAF_MENU_NODE *menu_item, //for this item + const char *new_label); //New label + + void set_toggle( //Change value & show + TOGGLE_MENU_LEAF *menu_item, //for this item + BOOL8 new_value); + + WINDOW window() { //Return window handle + return fd; + } +}; +extern INT_VAR_H (editor_cmdwin_width, 950, +"CmdWin max non scrollable width"); +extern INT_VAR_H (editor_cmdwin_height, 550, +"CmdWin max non scrollable height"); +extern INT_VAR_H (editor_cmdwin_xpos1, 20, "X pos of first command window"); +extern INT_VAR_H (editor_cmdwin_ypos1, 20, "Y pos of first command window"); +extern INT_VAR_H (editor_cmdwin_xoffset, 30, +"X offset between command windws"); +extern INT_VAR_H (editor_cmdwin_yoffset, 30, +"Y offset between command windws"); +#endif diff --git a/display/pagewalk.cpp b/display/pagewalk.cpp new file mode 100644 index 0000000000..cce0cf3fda --- /dev/null +++ b/display/pagewalk.cpp @@ -0,0 +1,666 @@ +/********************************************************************** + * File: pagewalk.cpp (Formerly walkers.c) + * Description: Block list processors + * Author: Phil Cheatle + * Created: Thu Oct 10 16:25:24 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "pagewalk.h" + +#define EXTERN + +EXTERN BOOL_VAR (current_word_quit, FALSE, "Stop processing this word"); +DLLSYM BOOL_VAR (selection_quit, FALSE, "Stop processing this selection"); + +/********************************************************************** + * block_list_bounding_box() + * + * Scan block list to find the bounding box of all blocks. + **********************************************************************/ + +BOX block_list_bounding_box( //find bounding box + BLOCK_LIST *block_list //of this block list + ) { + BLOCK_IT block_it(block_list); + BOX enclosing_box; + + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) + enclosing_box += block_it.data ()->bounding_box (); + return enclosing_box; +} + + +/********************************************************************** + * block_list_compress() + * + * Pack a block list to occupy a smaller space by compressing each block and + * moving the compressed blocks one above the other. + * The compressed block list has the same top left point as the uncompressed + * first. Blocks are reordered so that the source names are in alphabetic + * order. (This gathers together, but does not combine, blocks from the same + * file.) + * The enclosing box of the compressed block list is returned. + **********************************************************************/ + +const BOX block_list_compress( //shuffle up blocks + BLOCK_LIST *block_list) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ICOORD initial_top_left; + ICOORD block_spacing (0, BLOCK_SPACING); + BOX enclosing_box; //for full display + + initial_top_left = block_it.data ()->bounding_box ().topleft (); + //group srcfile blks + block_it.sort (block_name_order); + + /* Compress the target block list into an area starting from the top left of + the first block on the list */ + + enclosing_box = BOX (initial_top_left, initial_top_left); + enclosing_box.move_bottom_edge (BLOCK_SPACING); + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + block->compress (enclosing_box.botleft () - block_spacing - + block->bounding_box ().topleft ()); + enclosing_box += block->bounding_box (); + } + return enclosing_box; +} + + +/********************************************************************** + * block_list_move() + * + * Move all the blocks in the list by a vector + **********************************************************************/ + +void block_list_move( //move + BLOCK_LIST *block_list, //this list + ICOORD vec //by this vector + ) { + BLOCK_IT block_it(block_list); + + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) + block_it.data ()->move (vec); +} + + +/********************************************************************** + * block_name_order() + * + * Block comparator used to sort a block list so that blocks from the same + * filename are located together, and blocks from the same file are ordered + * by vertical position. + **********************************************************************/ + +int block_name_order( //sort blocks + const void *block1p, //ptr to ptr to block1 + const void *block2p //ptr to ptr to block2 + ) { + int result; + BLOCK *block1 = *(BLOCK **) block1p; + BLOCK *block2 = *(BLOCK **) block2p; + + result = strcmp (block1->name (), block2->name ()); + if (result == 0) + result = block2->bounding_box ().top () - block1->bounding_box ().top (); + return result; +} + + +/********************************************************************** + * process_all_blobs() + * + * Walk the current block list applying the specified blob processor function + * to all blobs + **********************************************************************/ + +void +process_all_blobs ( //process blobs +BLOCK_LIST * block_list, //blocks to check +BOOL8 blob_processor ( //function to call + //function to call +BLOCK *, ROW *, WERD *, PBLOB *), BOOL8 c_blob_processor ( +BLOCK +*, +ROW +*, +WERD +*, +C_BLOB +*)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + C_BLOB_IT c_blob_it; + C_BLOB *c_blob; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->flag (W_POLYGON)) { + if (blob_processor != NULL) { + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (!blob_processor (block, row, word, blob) || + selection_quit) + return; + } + } + } + else { + if (c_blob_processor != NULL) { + c_blob_it.set_to_list (word->cblob_list ()); + for (c_blob_it.mark_cycle_pt (); + !c_blob_it.cycled_list (); c_blob_it.forward ()) { + c_blob = c_blob_it.data (); + if (!c_blob_processor (block, row, word, c_blob) || + selection_quit) + return; + } + } + } + } + } + } +} + + +/********************************************************************** + * process_selected_blobs() + * + * Walk the current block list applying the specified blob processor function + * to each selected blob + **********************************************************************/ + +void +process_selected_blobs ( //process blobs +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 blob_processor ( + //function to call +BLOCK *, ROW *, WERD *, PBLOB *), BOOL8 c_blob_processor ( +BLOCK +*, +ROW +*, +WERD +*, +C_BLOB +*)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + C_BLOB_IT c_blob_it; + C_BLOB *c_blob; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->bounding_box ().overlap (selection_box)) { + if (word->flag (W_POLYGON)) { + if (blob_processor != NULL) { + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + if (blob->bounding_box (). + overlap (selection_box)) { + if (!blob_processor + (block, row, word, blob) + || selection_quit) + return; + } + } + } + } + else { + if (c_blob_processor != NULL) { + c_blob_it.set_to_list (word->cblob_list ()); + for (c_blob_it.mark_cycle_pt (); + !c_blob_it.cycled_list (); + c_blob_it.forward ()) { + c_blob = c_blob_it.data (); + if (c_blob-> + bounding_box (). + overlap (selection_box)) { + if (!c_blob_processor + (block, row, word, c_blob) + || selection_quit) + return; + } + } + } + } + } + } + } + } + } + } +} + + +/********************************************************************** + * process_all_words() + * + * Walk the current block list applying the specified word processor function + * to all words + **********************************************************************/ + +void +process_all_words ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 word_processor ( //function to call +BLOCK *, ROW *, WERD *)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (!word_processor (block, row, word) || selection_quit) + return; + } + } + } +} + + +/********************************************************************** + * process_selected_words() + * + * Walk the current block list applying the specified word processor function + * to each word selected. + **********************************************************************/ + +void +process_selected_words ( //process words +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 word_processor ( +BLOCK *, +ROW *, +WERD *)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->bounding_box ().overlap (selection_box)) { + if (!word_processor (block, row, word) || + selection_quit) + return; + } + } + } + } + } + } +} + + +/********************************************************************** + * process_all_words_it() PASS ITERATORS + * + * Walk the current block list applying the specified word processor function + * to all words + **********************************************************************/ + +void +process_all_words_it ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 word_processor ( //function to call +BLOCK *, +ROW *, +WERD *, +BLOCK_IT &, +ROW_IT &, WERD_IT &)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (!word_processor + (block, row, word, block_it, row_it, word_it) + || selection_quit) + return; + } + } + } +} + + +/********************************************************************** + * process_selected_words_it() PASS ITERATORS + * + * Walk the current block list applying the specified word processor function + * to each word selected. + **********************************************************************/ + +void +process_selected_words_it ( //process words +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 word_processor ( +BLOCK +*, +ROW *, +WERD +*, +BLOCK_IT +&, +ROW_IT +&, +WERD_IT +&)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->bounding_box ().overlap (selection_box)) { + if (!word_processor (block, row, word, + block_it, row_it, word_it) || + selection_quit) + return; + } + } + } + } + } + } +} + + +/********************************************************************** + * process_all_blocks() + * + * Walk the current block list applying the specified block processor function + * to each block. + **********************************************************************/ + +void +process_all_blocks ( //process blocks +BLOCK_LIST * block_list, //blocks to check +BOOL8 block_processor ( //function to call +BLOCK *)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (!block_processor (block) || selection_quit) + return; + } +} + + +/********************************************************************** + * process_selected_blocks() + * + * Walk the current block list applying the specified block processor function + * to each block selected. + **********************************************************************/ + +void +process_selected_blocks ( //process blocks +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 block_processor ( +BLOCK +*)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + if (!block_processor (block) || selection_quit) + return; + } + } +} + + +/********************************************************************** + * process_all_rows() + * + * Walk the current block list applying the specified row processor function + * to all rows + **********************************************************************/ + +void +process_all_rows ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 row_processor ( //function to call +BLOCK *, ROW *)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (!row_processor (block, row) || selection_quit) + return; + } + } +} + + +/********************************************************************** + * process_selected_rows() + * + * Walk the current block list applying the specified row processor function + * to each row selected. + **********************************************************************/ + +void +process_selected_rows ( //process rows +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 row_processor ( +BLOCK *, +ROW *)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + if (!row_processor (block, row) || selection_quit) + return; + } + } + } + } +} + + +/********************************************************************** + * process_all_rows_it() PASS ITERATORS + * + * Walk the current block list applying the specified row processor function + * to all rows + **********************************************************************/ + +void +process_all_rows_it ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 row_processor ( //function to call +BLOCK *, +ROW *, BLOCK_IT &, ROW_IT &)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (!row_processor (block, row, block_it, row_it) || selection_quit) + return; + } + } +} + + +/********************************************************************** + * process_selected_rows_it() PASS ITERATORS + * + * Walk the current block list applying the specified row processor function + * to each row selected. + **********************************************************************/ + +void +process_selected_rows_it ( //process rows +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 row_processor ( +BLOCK *, +ROW *, +BLOCK_IT +&, +ROW_IT +&)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + if (!row_processor (block, row, block_it, row_it) || + selection_quit) + return; + } + } + } + } +} diff --git a/display/pagewalk.h b/display/pagewalk.h new file mode 100644 index 0000000000..0dee31ab0f --- /dev/null +++ b/display/pagewalk.h @@ -0,0 +1,155 @@ +/********************************************************************** + * File: pagewalk.h (Formerly walkers.h) + * Description: Structure processors + * Author: Phil Cheatle + * Created: Thu Oct 10 16:25:24 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef PAGEWALK_H +#define PAGEWALK_H + +#include "ocrblock.h" +#include "ocrrow.h" +#include "werd.h" +#include "polyblob.h" +#include "stepblob.h" +#include "rect.h" +#include "varable.h" +#include "notdll.h" + +#define BLOCK_SPACING 20 + +extern BOOL_VAR_H (current_word_quit, FALSE, "Stop processing this word"); +extern DLLSYM BOOL_VAR_H (selection_quit, FALSE, +"Stop processing this selection"); +BOX block_list_bounding_box( //find bounding box + BLOCK_LIST *block_list //of this block list + ); +const BOX block_list_compress( //shuffle up blocks + BLOCK_LIST *block_list); +void block_list_move( //move + BLOCK_LIST *block_list, //this list + ICOORD vec //by this vector + ); +int block_name_order( //sort blocks + const void *block1p, //ptr to ptr to block1 + const void *block2p //ptr to ptr to block2 + ); +void process_all_blobs ( //process blobs +BLOCK_LIST * block_list, //blocks to check +BOOL8 blob_processor ( //function to call + //function to call +BLOCK *, ROW *, WERD *, PBLOB *), BOOL8 c_blob_processor ( +BLOCK +*, +ROW +*, +WERD +*, +C_BLOB +*)); +void process_selected_blobs ( //process blobs +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 blob_processor ( + //function to call +BLOCK *, ROW *, WERD *, PBLOB *), BOOL8 c_blob_processor ( +BLOCK +*, +ROW +*, +WERD +*, +C_BLOB +*)); +void process_all_words ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 word_processor ( //function to call +BLOCK *, ROW *, WERD *)); +void process_selected_words ( //process words +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 word_processor ( +BLOCK +*, +ROW +*, +WERD +*)); +void process_all_words_it ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 word_processor ( //function to call +BLOCK *, +ROW *, +WERD *, +BLOCK_IT &, +ROW_IT &, WERD_IT &)); +void process_selected_words_it ( //process words +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 word_processor ( +BLOCK +*, +ROW +*, +WERD +*, +BLOCK_IT +&, +ROW_IT +&, +WERD_IT +&)); +void process_all_blocks ( //process blocks +BLOCK_LIST * block_list, //blocks to check +BOOL8 block_processor ( //function to call +BLOCK *)); +void process_selected_blocks ( //process blocks +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 block_processor ( +BLOCK +*)); +void process_all_rows ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 row_processor ( //function to call +BLOCK *, ROW *)); +void process_selected_rows ( //process rows +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 row_processor ( +BLOCK +*, +ROW +*)); +void process_all_rows_it ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 row_processor ( //function to call +BLOCK *, +ROW *, +BLOCK_IT &, ROW_IT &)); +void process_selected_rows_it ( //process rows +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 row_processor ( +BLOCK +*, +ROW +*, +BLOCK_IT +&, +ROW_IT +&)); +#endif diff --git a/display/pgedit.cpp b/display/pgedit.cpp new file mode 100644 index 0000000000..dd60348bb8 --- /dev/null +++ b/display/pgedit.cpp @@ -0,0 +1,1938 @@ +/********************************************************************** + * File: pgedit.cpp (Formerly pgeditor.c) + * Description: Page structure file editor + * Author: Phil Cheatle + * Created: Thu Oct 10 16:25:24 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include +#include "cmndwin.h" +#include "genblob.h" +#include "pgedit.h" +#include "pgeditx.h" +#include "tessio.h" +#include "tessout.h" +#include "submen.h" +#include "varblwin.h" +#include "tordmain.h" +#include "statistc.h" +#include "debugwin.h" +#include "showim.h" +#include "mainblk.h" +#include "evnts.h" + +#define ASC_HEIGHT (2 * bln_baseline_offset + bln_x_height) +#define X_HEIGHT (bln_baseline_offset + bln_x_height) +#define BL_HEIGHT bln_baseline_offset +#define DESC_HEIGHT 0 +#define MAXSPACING 128 /*max expected spacing in pix */ + +const ERRCODE EMPTYBLOCKLIST = "No blocks to edit"; +extern IMAGE page_image; + +enum CMD_EVENTS +{ + NULL_CMD_EVENT, + DELETE_CMD_EVENT, + COPY_CMD_EVENT, + CHANGE_DISP_CMD_EVENT, + CHANGE_TEXT_CMD_EVENT, + TOGGLE_SEG_CMD_EVENT, + DUMP_WERD_CMD_EVENT, + SHOW_POINT_CMD_EVENT, + ROW_SPACE_STAT_CMD_EVENT, + BLOCK_SPACE_STAT_CMD_EVENT, + SHOW_BLN_WERD_CMD_EVENT, + SEGMENT_WERD_CMD_EVENT, + BOUNDING_BOX_CMD_EVENT, + CORRECT_TEXT_CMD_EVENT, + POLYGONAL_CMD_EVENT, + BL_NORM_CMD_EVENT, + BITMAP_CMD_EVENT, + TIDY_CMD_EVENT, + VIEW_CMD_EVENT, + IMAGE_CMD_EVENT, + BLOCKS_CMD_EVENT, + BASELINES_CMD_EVENT, + WRITE_CMD_EVENT, + SMD_CMD_EVENT, + NEW_SOURCE_CMD_EVENT, + UNIFORM_DISP_CMD_EVENT, + REFRESH_CMD_EVENT, + QUIT_CMD_EVENT +}; + +#define EXTENDED_MODES_BASE 1000 //for extended cmd ids +#define EXTENDED_OTHER_BASE 2000 + +/********************************************************************** + * + * Some global data + * + **********************************************************************/ + +RADIO_MENU *modes_menu_item; +RADIO_MENU_LEAF *change_display_menu_item; +SIMPLE_MENU_LEAF *view_menu_item; +RADIO_MENU_LEAF *copy_menu_item; +VARIABLE_MENU_LEAF *write_menu_item; + +#ifdef __UNIX__ +FILE *debug_window = NULL; //opened on demand +#endif + //baseline norm words +WINDOW bln_word_window = NO_WINDOW; + +CMD_EVENTS mode = CHANGE_DISP_CMD_EVENT; + //Selected words op + +BITS16 word_display_mode; +BOOL8 display_image = FALSE; +BOOL8 display_blocks = FALSE; +BOOL8 display_baselines = FALSE; +BOOL8 viewing_source = TRUE; + +BLOCK_LIST *source_block_list = NULL; //image blocks +BLOCK_LIST target_block_list; //target blocks +BLOCK_LIST *other_block_list = &target_block_list; + +BOOL8 source_changed = FALSE; //Changes not saved +BOOL8 target_changed = FALSE; //Changes not saved +BOOL8 *other_image_changed = &target_changed; + +/* Public globals */ + +#define EXTERN + + //image window +EXTERN WINDOW image_win = NO_WINDOW; +EXTERN COMMAND_WINDOW *command_window; + +EXTERN BLOCK_LIST *current_block_list = NULL; +EXTERN BOOL8 *current_image_changed = &source_changed; +EXTERN void (*show_pt_handler) (GRAPHICS_EVENT *) = NULL; + +/* Variables */ + +EXTERN STRING_VAR (editor_image_win_name, "EditorImage", +"Editor image window name"); +EXTERN INT_VAR (editor_image_xpos, 590, "Editor image X Pos"); +EXTERN INT_VAR (editor_image_ypos, 10, "Editor image Y Pos"); +EXTERN INT_VAR (editor_image_height, 680, "Editor image height"); +EXTERN INT_VAR (editor_image_width, 655, "Editor image width"); +EXTERN INT_VAR (editor_image_word_bb_color, BLUE, "Word bounding box colour"); +EXTERN INT_VAR (editor_image_blob_bb_color, YELLOW, +"Blob bounding box colour"); +EXTERN INT_VAR (editor_image_text_color, WHITE, "Correct text colour"); + +EXTERN STRING_VAR (editor_dbwin_name, "EditorDBWin", +"Editor debug window name"); +EXTERN INT_VAR (editor_dbwin_xpos, 50, "Editor debug window X Pos"); +EXTERN INT_VAR (editor_dbwin_ypos, 500, "Editor debug window Y Pos"); +EXTERN INT_VAR (editor_dbwin_height, 24, "Editor debug window height"); +EXTERN INT_VAR (editor_dbwin_width, 80, "Editor debug window width"); + +EXTERN STRING_VAR (editor_word_name, "BlnWords", "BL normalised word window"); +EXTERN INT_VAR (editor_word_xpos, 60, "Word window X Pos"); +EXTERN INT_VAR (editor_word_ypos, 510, "Word window Y Pos"); +EXTERN INT_VAR (editor_word_height, 240, "Word window height"); +EXTERN INT_VAR (editor_word_width, 655, "Word window width"); + +EXTERN double_VAR (editor_smd_scale_factor, 1.0, "Scaling for smd image"); + +/********************************************************************** + * add_word() + * + * Inserts the a word into a specified block list. The list is searched for a + * block and row of the same file as those of the word to be added, which + * contain the bounding box of the word. If such a row is found, the new + * word is inserted into the row in the correct X order. If the + * block is found, but not the row, a copy of the word's old row is added to + * the block in the correct Y order, and the word is put in that row. + * If neither the row nor the block are found, then the word's old block is + * copied with only the word's row. It is added to the block list in the + * correct Y order. + **********************************************************************/ + +void add_word( //to block list + WERD *word, //word to be added + ROW *src_row, //source row + BLOCK *src_block, //source block + BLOCK_LIST *dest_block_list //add to this + ) { + BLOCK_IT block_it(dest_block_list); + BLOCK *block; //current block + BLOCK *dest_block = NULL; //destination block + ROW_IT row_it; + ROW *row; //destination row + ROW *dest_row = NULL; //destination row + WERD_IT word_it; + BOX word_box = word->bounding_box (); + BOX insert_point_word_box; + BOOL8 seen_blocks_for_current_file = FALSE; + + block_it.mark_cycle_pt (); + while (!block_it.cycled_list () && (dest_block == NULL)) { + block = block_it.data (); + if ((block->bounding_box ().contains (word_box)) && + (strcmp (block->name (), src_block->name ()) == 0)) { + dest_block = block; //found dest block + row_it.set_to_list (block->row_list ()); + row_it.mark_cycle_pt (); + while ((!row_it.cycled_list ()) && (dest_row == NULL)) { + row = row_it.data (); + if (row->bounding_box ().contains (word_box)) + dest_row = row; //found dest row + else + row_it.forward (); + } + } + else + block_it.forward (); + } + + if (dest_block == NULL) { //make a new one + dest_block = new BLOCK; + *dest_block = *src_block; + + block_it.set_to_list (dest_block_list); + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + + if (!seen_blocks_for_current_file && + (strcmp (block->name (), dest_block->name ()) == 0)) + seen_blocks_for_current_file = TRUE; + + if (seen_blocks_for_current_file && + ((strcmp (block->name (), dest_block->name ()) != 0) || + (block->bounding_box ().top () < + dest_block->bounding_box ().top ()))) + break; + } + + if (block_it.cycled_list ()) + //didn't find insrt pt + block_it.add_to_end (dest_block); + else + //did find insert pt + block_it.add_before_stay_put (dest_block); + } + + if (dest_row == NULL) { //make a new one + dest_row = new ROW; + *dest_row = *src_row; + + row_it.set_to_list (dest_block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + if (row_it.data ()->bounding_box ().top () < + dest_row->bounding_box ().top ()) + break; + } + + if (row_it.cycled_list ()) + //didn't find insrt pt + row_it.add_to_end (dest_row); + else + //did find insert pt + row_it.add_before_stay_put (dest_row); + } + + /* dest_block and dest_row are now found or built and inserted as necessesary + so add the word to dest row */ + + word_it.set_to_list (dest_row->word_list ()); + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + if (word_it.data ()->bounding_box ().right () >= word_box.left ()) + break; + } + + if (word_it.cycled_list ()) + word_it.add_to_end (word); //didn't find insrt pt + else { //did find insert pt + insert_point_word_box = word_it.data ()->bounding_box (); + if (insert_point_word_box.contains (word_box) || + word_box.contains (insert_point_word_box)) + command_window-> + msg + ("Refusing to add words which obliterate, or are obliterated by, others"); + else { + if (word_it.data ()->bounding_box ().left () > + word->bounding_box ().left ()) + //infront of insert pt + word_it.add_before_stay_put (word); + else + //behind insert pt + word_it.add_after_stay_put (word); + } + } +} + + +/********************************************************************** + * bln_word_window_handle() + * + * Return a WINDOW for the word window, creating it if necessary + **********************************************************************/ + +WINDOW bln_word_window_handle() { //return handle + //not opened yet + if (bln_word_window == NO_WINDOW) { + pgeditor_msg ("Creating BLN word window..."); + // xmin, xmax + bln_word_window = create_window (editor_word_name.string (), SCROLLINGWIN, editor_word_xpos, editor_word_ypos, editor_word_width, editor_word_height, -2000.0, 2000.0, + DESC_HEIGHT - 30.0f, + ASC_HEIGHT + 30.0f, + // ymin, ymax + TRUE, FALSE, FALSE, FALSE); + // down event only + + pgeditor_msg ("Creating BLN word window...Done"); + } + return bln_word_window; +} + + +/********************************************************************** + * build_image_window() + * + * Destroy the existing image window if there is one. Work out how big the + * new window needs to be. Create it and re-display. + **********************************************************************/ + +void build_image_window(BOX page_bounding_box) { + if (image_win != NO_WINDOW) + destroy_window(image_win); + + // xmin + image_win = create_window (editor_image_win_name.string (), SCROLLINGWIN, editor_image_xpos, editor_image_ypos, editor_image_width, editor_image_height, 0.0, + (float) page_bounding_box.right () + 1, + // xmax + 0.0, // ymin + (float) page_bounding_box.top () + 1, + // ymax + TRUE, FALSE, TRUE, FALSE); //down and up only +} + + +/********************************************************************** + * build_menu() + * + * Construct the menu tree used by the command window + **********************************************************************/ + +MENU_ROOT *build_menu() { + NON_RADIO_MENU *parent_menu; + MENU_ROOT *root_menu_item; + + root_menu_item = new MENU_ROOT (); + + modes_menu_item = new RADIO_MENU ("MODES"); + root_menu_item->add_child (modes_menu_item); + + change_display_menu_item = new RADIO_MENU_LEAF ("Change Display", + CHANGE_DISP_CMD_EVENT); + modes_menu_item->add_child (change_display_menu_item); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Delete", + DELETE_CMD_EVENT)); + copy_menu_item = new RADIO_MENU_LEAF ("Copy to TARGET", COPY_CMD_EVENT); + modes_menu_item->add_child (copy_menu_item); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Change Text", + CHANGE_TEXT_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Toggle Correct Seg Flg", + TOGGLE_SEG_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Dump Word", + DUMP_WERD_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Show Point", + SHOW_POINT_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Row gaps hist", + ROW_SPACE_STAT_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Block gaps hist", + BLOCK_SPACE_STAT_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Show BL Norm Word", + SHOW_BLN_WERD_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Re-Segment Word", + SEGMENT_WERD_CMD_EVENT)); + + parent_menu = new NON_RADIO_MENU ("DISPLAY"); + root_menu_item->add_child (parent_menu); + + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Bounding Boxes", + BOUNDING_BOX_CMD_EVENT, + TRUE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Correct Text", + CORRECT_TEXT_CMD_EVENT, + FALSE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Polygonal Approx", + POLYGONAL_CMD_EVENT, FALSE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Baseline Normalised", + BL_NORM_CMD_EVENT, FALSE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Edge Steps", + BITMAP_CMD_EVENT, FALSE)); + + parent_menu = new NON_RADIO_MENU ("OTHER"); + root_menu_item->add_child (parent_menu); + + parent_menu->add_child (new SIMPLE_MENU_LEAF ("Quit", QUIT_CMD_EVENT)); + parent_menu->add_child (new SIMPLE_MENU_LEAF ("Tidy Target", + TIDY_CMD_EVENT)); + view_menu_item = new SIMPLE_MENU_LEAF ("View TARGET", VIEW_CMD_EVENT); + parent_menu->add_child (view_menu_item); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Show Image", + IMAGE_CMD_EVENT, FALSE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("ShowBlock Outlines", + BLOCKS_CMD_EVENT, FALSE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Show Baselines", + BASELINES_CMD_EVENT, FALSE)); + write_menu_item = new VARIABLE_MENU_LEAF ("Write File", + WRITE_CMD_EVENT, + imagebasename.string ()); + parent_menu->add_child (write_menu_item); + parent_menu->add_child (new SIMPLE_MENU_LEAF ("Make SMD Image", + SMD_CMD_EVENT)); + parent_menu->add_child (new VARIABLE_MENU_LEAF ("New Source File", + NEW_SOURCE_CMD_EVENT, + imagebasename.string ())); + parent_menu->add_child (new SIMPLE_MENU_LEAF ("Uniform Display", + UNIFORM_DISP_CMD_EVENT)); + parent_menu->add_child (new SIMPLE_MENU_LEAF ("Refresh Display", + REFRESH_CMD_EVENT)); + + //Call driver program + extend_menu(modes_menu_item, + EXTENDED_MODES_BASE, + parent_menu, + EXTENDED_OTHER_BASE); + return root_menu_item; +} + + +/********************************************************************** + * debug_window_handle() + * + * Return a FILE* for the debug window, creating it if necessary + **********************************************************************/ + +void debug_window_handle() { //return handle + // if ( debug_winth == NULL ) //not opened yet + // { + // pgeditor_msg( "Creating debug window..." ); + // create_debug_window(); + pgeditor_msg ("Creating debug window...Done"); + // } +} + + +/********************************************************************** + * display_bln_lines() + * + * Display normalised baseline, x-height, ascender limit and descender limit + **********************************************************************/ + +void display_bln_lines(WINDOW window, + COLOUR colour, + float scale_factor, + float y_offset, + float minx, + float maxx) { + line_color_index(window, colour); + line_type(window, SOLID); + move2d (window, minx, y_offset + scale_factor * DESC_HEIGHT); + draw2d (window, maxx, y_offset + scale_factor * DESC_HEIGHT); + move2d (window, minx, y_offset + scale_factor * BL_HEIGHT); + draw2d (window, maxx, y_offset + scale_factor * BL_HEIGHT); + move2d (window, minx, y_offset + scale_factor * X_HEIGHT); + draw2d (window, maxx, y_offset + scale_factor * X_HEIGHT); + move2d (window, minx, y_offset + scale_factor * ASC_HEIGHT); + draw2d (window, maxx, y_offset + scale_factor * ASC_HEIGHT); + +} + + +/********************************************************************** + * do_new_source() + * + * Change to another source file. Automatically tidy page first + * + **********************************************************************/ + +void do_new_source( //serialise + char *name //file name + ) { + FILE *infp; //input file + char msg_str[MAX_CHARS + 1]; + STRING name_str(name); + char response_str[MAX_CHARS + 1]; + char *token; //first response token + + if (source_changed) { + response_str[0] = '\0'; + command_window->prompt ("Source changes will be LOST. Continue? (Y/N)", + response_str); + token = strtok (response_str, " "); + if (tolower (token[0]) != 'y') + return; + } + + //if not file exists + if (!(infp = fopen (name, "r"))) { + sprintf (msg_str, "Cant open file " "%s" "", name); + command_window->msg (msg_str); + return; + } + + fclose(infp); + sprintf (msg_str, "Reading file " "%s" "...", name); + command_window->msg (msg_str); + source_block_list->clear (); + //appends to SOURCE + pgeditor_read_file(name_str, source_block_list); + source_changed = FALSE; + command_window->msg ("Doing automatic Tidy Target..."); + viewing_source = FALSE; //Force viewing source + do_tidy_cmd(); + command_window->msg ("Doing automatic Tidy Target...Done"); +} + + +/********************************************************************** + * do_re_display() + * + * Redisplay page + **********************************************************************/ + +void + //function to call +do_re_display (BOOL8 word_painter ( +BLOCK *, ROW *, WERD *)) { + BLOCK_IT block_it(current_block_list); + BLOCK *block; + int block_count = 1; + + ROW_IT row_it; + ROW *row; + + WERD_IT word_it; + WERD *word; + + clear_view_surface(image_win); + if (display_image) { + show_sub_image (&page_image, 0, 0, + page_image.get_xsize (), page_image.get_ysize (), + image_win, 0, 0); + } + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + word_painter(block, row, word); + } + if (display_baselines) + row->plot_baseline (image_win, GREEN); + } + if (display_blocks) + block->plot (image_win, block_count++, RED); + } +} + + +/********************************************************************** + * do_tidy_cmd() + * + * Tidy TARGET page + **********************************************************************/ + +const BOX do_tidy_cmd() { //tidy + ICOORD shift_vector; + BOX tidy_box; //Just the tidy area + BOX source_box; //source file area + + source_box = block_list_bounding_box (source_block_list); + //find src area + + if (!target_block_list.empty ()) { + tidy_box = block_list_compress (&target_block_list); + + /* Shift tidied target above the source image area. */ + + shift_vector = ICOORD (0, source_box.top () + BLOCK_SPACING) + - tidy_box.botleft (); + block_list_move(&target_block_list, shift_vector); + tidy_box.move (shift_vector); + } + source_box += tidy_box; + //big enough for both + build_image_window(source_box); + do_view_cmd(); + return tidy_box; +} + + +/********************************************************************** + * do_view_cmd() + * + * View TARGET/View SOURCE command + **********************************************************************/ + +void do_view_cmd() { + viewing_source = !viewing_source; + clear_view_surface(image_win); + if (viewing_source) { + current_block_list = source_block_list; + current_image_changed = &source_changed; + other_block_list = &target_block_list; + other_image_changed = &target_changed; + do_re_display(&word_display); + + command_window->replace_menu_text (view_menu_item, "View TARGET"); + command_window->replace_menu_text (copy_menu_item, "Copy to TARGET"); + write_menu_item->replace_value (imagebasename.string ()); + } + else { + current_block_list = &target_block_list; + current_image_changed = &target_changed; + other_block_list = source_block_list; + other_image_changed = &source_changed; + do_re_display(&word_display); + + command_window->replace_menu_text (view_menu_item, "View SOURCE"); + command_window->replace_menu_text (copy_menu_item, ""); + write_menu_item->replace_value ((imagebasename + ".bits.pg").string ()); + } +} + + +/********************************************************************** + * do_write_file() + * + * Serialise a block list to file + * + * If writing image, tidy page and move to (0,0) first + **********************************************************************/ + +void do_write_file( //serialise + char *name //file name + ) { + FILE *infp; //input file + char msg_str[MAX_CHARS + 1]; + char response_str[MAX_CHARS + 1]; + char *token; //first response token + BOX enclosing_box; + + //if file exists + if ((infp = fopen (name, "r")) != NULL) { + fclose(infp); + sprintf (msg_str, "Overwrite file " "%s" "? (Y/N)", name); + response_str[0] = '\0'; + if (!command_window->prompt (msg_str, response_str)) + return; + token = strtok (response_str, " "); + if (tolower (token[0]) != 'y') + return; // dont write + } + + infp = fopen (name, "w"); //can we write to it? + if (infp == NULL) { + sprintf (msg_str, "Cant write to file " "%s" "", name); + command_window->msg (msg_str); + return; + } + fclose(infp); + + if (!viewing_source && !target_block_list.empty ()) { + //Tidy & move to (0,0) + command_window->msg ("Automatic tidy..."); + viewing_source = TRUE; //Stay viewing target! + enclosing_box = do_tidy_cmd (); + block_list_move (&target_block_list, -enclosing_box.botleft ()); + command_window->msg ("Writing file..."); + pgeditor_write_file(name, &target_block_list); + //move back + block_list_move (&target_block_list, + enclosing_box.botleft ()); + } + else { + command_window->msg ("Writing file..."); + pgeditor_write_file(name, current_block_list); + } + command_window->msg ("Writing file...Done"); + *current_image_changed = FALSE; +} + + +void smd_cmd() { + char response_str[MAX_CHARS + 1]; + WINDOW display_window; //temp + ICOORD tr, bl; + BOX page_box = block_list_bounding_box (current_block_list); + + bl = ICOORD (0, 0); + tr = ICOORD (page_image.get_xsize () + 1, page_image.get_ysize () + 1); + page_box += BOX (bl, tr); + + strcpy (response_str, imagebasename.string ()); + strcpy (response_str + imagebasename.length (), ".smd.tif"); + command_window->prompt ("SMD File Name?", response_str); + + display_window = image_win; + // xmin + image_win = create_window (response_str, SMDWINDOW, 0, 0, (INT16) (page_box.width () * editor_smd_scale_factor), (INT16) (page_box.height () * editor_smd_scale_factor), 0.0, + page_box.width (), // xmax + 0.0, // ymin + page_box.height (), // ymax + FALSE, FALSE, FALSE, FALSE); //down and up only + do_re_display(&word_display); + destroy_window(image_win); //Dumps sbd file + image_win = display_window; +} + + +/********************************************************************** + * pgeditor_main() + * + * Top level editor operation: + * Read events and send them to the appropriate command processor. + * + **********************************************************************/ + +void pgeditor_main(BLOCK_LIST *blocks) { + GRAPHICS_EVENT event; + INT32 cmd_event = 0; + char new_value[MAX_CHARS + 1]; + BOOL8 exit = FALSE; + + source_block_list = blocks; + current_block_list = blocks; + if (current_block_list->empty ()) + return; + + command_window = new COMMAND_WINDOW ("WordEditorCmd", build_menu ()); + build_image_window (block_list_bounding_box (source_block_list)); + do_re_display(&word_display); + word_display_mode.turn_on_bit (DF_BOX); + + while (!exit) { + overlap_picture_ops(TRUE); + await_event (0, //all windows + TRUE, //wait for event + ANY_EVENT, &event); + //Command win event + if (event.fd == command_window->window ()) { + command_window->msg (""); //Clear old message + command_window->event (event, &cmd_event, new_value); + exit = process_cmd_win_event (cmd_event, new_value); + } + else { + if (event.fd == image_win) + process_image_event(event); + else + pgeditor_show_point(&event); + } + current_word_quit.set_value (FALSE); + selection_quit.set_value (FALSE); + //replot all var wins + VARIABLES_WINDOW::plot_all(); + } +} + + +/********************************************************************** + * pgeditor_msg() + * + * Display a message - in the command window if there is one, or to stdout + **********************************************************************/ + +void pgeditor_msg( //message display + const char *msg) { + if (command_window == NO_WINDOW) { + tprintf(msg); + tprintf ("\n"); + } + else + command_window->msg (msg); +} + + +/********************************************************************** + * pgeditor_read_file() + * + * Deserialise source file + **********************************************************************/ + +void pgeditor_read_file( //of serialised file + STRING &name, + BLOCK_LIST *blocks //block list to add to + ) { + int c; //input character + FILE *infp; //input file + BLOCK_IT block_it(blocks); //iterator + BLOCK *block; //current block + + ICOORD page_tr; //topright of page + + char *filename_extension; + + block_it.move_to_last (); + + // ptr to last dot + filename_extension = strrchr (name.string (), '.'); + #ifdef __UNIX__ + /* TEXTROW* tessrows; + TBLOB* tessblobs; + TPOINT tess_tr; + + if (strcmp( filename_extension, ".r" ) == 0) + { + tprintf( "Converting from .r file format.\n" ); + tessrows = get_tess_row_file( name.string(), //get the row file + &tess_tr ); + page_tr = ICOORD( tess_tr.x, tess_tr.y ); + make_blocks_from_rows( tessrows, name.string(), //reconstruct blocks + page_tr, TRUE, &block_it ); + } + else if (strcmp( filename_extension, ".b" ) == 0) + { + tprintf( "Converting from .b file format.\n" ); + tessblobs = get_tess_blob_file( name.string(), //get the blob file + &tess_tr ); + page_tr = ICOORD( tess_tr.x, tess_tr.y ); + make_blocks_from_blobs( tessblobs, name.string(), + //reconstruct blocks + page_tr, FALSE,blocks); + } + else*/ + if (strcmp (filename_extension, ".pb") == 0) { + tprintf ("Converting from .pb file format.\n"); + //construct blocks + read_and_textord (name.string (), blocks); + } + else + #endif + if ((strcmp (filename_extension, ".pg") == 0) || + // read a .pg file + // or a .sp file + (strcmp (filename_extension, ".sp") == 0)) { + tprintf ("Reading %s file format.\n", filename_extension); + infp = fopen (name.string (), "r"); + if (infp == NULL) + CANTOPENFILE.error ("pgeditor_read_file", EXIT, name.string ()); + //can't open file + + while (((c = fgetc (infp)) != EOF) && (ungetc (c, infp) != EOF)) { + //get one + block = BLOCK::de_serialise (infp); + //add to list + block_it.add_after_then_move (block); + } + fclose(infp); + } else { + edges_and_textord (name.string (), blocks); + } +} + + +/********************************************************************** + * pgeditor_show_point() + * + * Display the coordinates of a point in the command window + **********************************************************************/ + +void pgeditor_show_point( //display coords + GRAPHICS_EVENT *event) { + char msg[160]; + + sprintf (msg, "Pointing at (%f, %f)", event->x, event->y); + command_window->msg (msg); +} + + +/********************************************************************** + * pgeditor_write_file() + * + * Serialise a block list to file + * + **********************************************************************/ + +void pgeditor_write_file( //serialise + char *name, //file name + BLOCK_LIST *blocks //block list to write + ) { + FILE *infp; //input file + BLOCK_IT block_it(blocks); //block iterator + BLOCK *block; //current block + ROW_IT row_it; //row iterator + + infp = fopen (name, "w"); //create output file + if (infp == NULL) + CANTCREATEFILE.error ("pgeditor_write_file", EXIT, name); + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.extract (); + + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) + //ensure correct + row_it.data ()->recalc_bounding_box (); + + block->serialise (infp); //serialize non-empty + block_it.add_after_then_move (block); + } + fclose(infp); +} + + +/********************************************************************** + * process_cmd_win_event() + * + * Process a command returned from the command window + * (Just call the appropriate command handler) + **********************************************************************/ + +BOOL8 process_cmd_win_event( //UI command semantics + INT32 cmd_event, //which menu item? + char *new_value //any prompt data + ) { + char msg[160]; + BOOL8 exit = FALSE; + char response_str[MAX_CHARS + 1]; + char *token; //first response token + + switch (cmd_event) { + case NULL_CMD_EVENT: + break; + + case VIEW_CMD_EVENT: + do_view_cmd(); + break; + case CHANGE_DISP_CMD_EVENT: + case DELETE_CMD_EVENT: + case CHANGE_TEXT_CMD_EVENT: + case TOGGLE_SEG_CMD_EVENT: + case DUMP_WERD_CMD_EVENT: + case SHOW_POINT_CMD_EVENT: + case ROW_SPACE_STAT_CMD_EVENT: + case BLOCK_SPACE_STAT_CMD_EVENT: + case SHOW_BLN_WERD_CMD_EVENT: + case SEGMENT_WERD_CMD_EVENT: + mode = (CMD_EVENTS) cmd_event; + break; + case COPY_CMD_EVENT: + mode = (CMD_EVENTS) cmd_event; + if (!viewing_source) + command_window->msg ("Can't COPY while viewing target!"); + break; + case BOUNDING_BOX_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit (DF_BOX); + else + word_display_mode.turn_off_bit (DF_BOX); + command_window->press_radio_button (modes_menu_item, + change_display_menu_item); + mode = CHANGE_DISP_CMD_EVENT; + break; + case CORRECT_TEXT_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit (DF_TEXT); + else + word_display_mode.turn_off_bit (DF_TEXT); + command_window->press_radio_button (modes_menu_item, + change_display_menu_item); + mode = CHANGE_DISP_CMD_EVENT; + break; + case POLYGONAL_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit (DF_POLYGONAL); + else + word_display_mode.turn_off_bit (DF_POLYGONAL); + command_window->press_radio_button (modes_menu_item, + change_display_menu_item); + mode = CHANGE_DISP_CMD_EVENT; + break; + case BL_NORM_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit (DF_BN_POLYGONAL); + else + word_display_mode.turn_off_bit (DF_BN_POLYGONAL); + command_window->press_radio_button (modes_menu_item, + change_display_menu_item); + mode = CHANGE_DISP_CMD_EVENT; + break; + case BITMAP_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.turn_on_bit (DF_EDGE_STEP); + else + word_display_mode.turn_off_bit (DF_EDGE_STEP); + command_window->press_radio_button (modes_menu_item, + change_display_menu_item); + mode = CHANGE_DISP_CMD_EVENT; + break; + case UNIFORM_DISP_CMD_EVENT: + do_re_display(&word_set_display); + *current_image_changed = TRUE; + break; + case WRITE_CMD_EVENT: + do_write_file(new_value); + break; + case SMD_CMD_EVENT: + smd_cmd(); + break; + case TIDY_CMD_EVENT: + if (!target_block_list.empty ()) { + viewing_source = TRUE; //Force viewing target + do_tidy_cmd(); + } + break; + case NEW_SOURCE_CMD_EVENT: + do_new_source(new_value); + break; + case IMAGE_CMD_EVENT: + display_image = (new_value[0] == 'T'); + do_re_display(&word_display); + break; + case BLOCKS_CMD_EVENT: + display_blocks = (new_value[0] == 'T'); + do_re_display(&word_display); + break; + case BASELINES_CMD_EVENT: + display_baselines = (new_value[0] == 'T'); + do_re_display(&word_display); + break; + case REFRESH_CMD_EVENT: + do_re_display(&word_display); + break; + case QUIT_CMD_EVENT: + if (source_changed || target_changed) { + response_str[0] = '\0'; + command_window->prompt ("Changes not saved. Exit anyway? (Y/N)", + response_str); + token = strtok (response_str, " "); + if (tolower (token[0]) == 'y') + exit = TRUE; + } + else + exit = TRUE; + break; + default: + if ((cmd_event >= EXTENDED_MODES_BASE) && + (cmd_event < EXTENDED_OTHER_BASE)) + mode = (CMD_EVENTS) cmd_event; + else { + if (cmd_event >= EXTENDED_OTHER_BASE) + extend_unmoded_commands (cmd_event - EXTENDED_OTHER_BASE, + new_value); + else { + sprintf (msg, "Unrecognised event " INT32FORMAT " (%s)", + cmd_event, new_value); + command_window->msg (msg); + } + } + break; + } + return exit; +} + + +/********************************************************************** + * process_image_event() + * + * User has done something in the image window - mouse down or up. Work out + * what it is and do something with it. + * If DOWN - just remember where it was. + * If UP - for each word in the selected area do the operation defined by + * the current mode. + **********************************************************************/ + +void process_image_event( //action in image win + GRAPHICS_EVENT event) { + static ICOORD down; + ICOORD up; + BOX selection_box; + char msg[80]; + + switch (event.type) { + case DOWN_EVENT: + down.set_x ((INT16) floor (event.x + 0.5)); + down.set_y ((INT16) floor (event.y + 0.5)); + if (mode == SHOW_POINT_CMD_EVENT) + show_point (current_block_list, event.x, event.y); + break; + + case UP_EVENT: + case SELECT_EVENT: + if (event.type == SELECT_EVENT) { + down.set_x ((INT16) floor (event.xmax + 0.5)); + down.set_y ((INT16) floor (event.ymax + 0.5)); + if (mode == SHOW_POINT_CMD_EVENT) + show_point (current_block_list, event.x, event.y); + } + if (mode != SHOW_POINT_CMD_EVENT) + command_window->msg ("");//Clear old message + up.set_x ((INT16) floor (event.x + 0.5)); + up.set_y ((INT16) floor (event.y + 0.5)); + selection_box = BOX (up, down); + + switch (mode) { + case CHANGE_DISP_CMD_EVENT: + process_selected_words(current_block_list, + selection_box, + &word_blank_and_set_display); + break; + case COPY_CMD_EVENT: + if (!viewing_source) + command_window->msg ("Can't COPY while viewing target!"); + else + process_selected_words(current_block_list, + selection_box, + &word_copy); + break; + case DELETE_CMD_EVENT: + process_selected_words_it(current_block_list, + selection_box, + &word_delete); + break; + case CHANGE_TEXT_CMD_EVENT: + process_selected_words(current_block_list, + selection_box, + &word_change_text); + break; + case TOGGLE_SEG_CMD_EVENT: + process_selected_words(current_block_list, + selection_box, + &word_toggle_seg); + break; + case DUMP_WERD_CMD_EVENT: + process_selected_words(current_block_list, + selection_box, + &word_dumper); + break; + case SHOW_BLN_WERD_CMD_EVENT: + process_selected_words(current_block_list, + selection_box, + &word_bln_display); + break; + case SEGMENT_WERD_CMD_EVENT: + re_segment_word(current_block_list, selection_box); + break; + case ROW_SPACE_STAT_CMD_EVENT: + row_space_stat(current_block_list, selection_box); + break; + case BLOCK_SPACE_STAT_CMD_EVENT: + block_space_stat(current_block_list, selection_box); + break; + case SHOW_POINT_CMD_EVENT: + break; //ignore up event + default: + if ((mode >= EXTENDED_MODES_BASE) && (mode < EXTENDED_OTHER_BASE)) + extend_moded_commands (mode - EXTENDED_MODES_BASE, selection_box); + else { + sprintf (msg, "Mode %d not yet implemented", mode); + command_window->msg (msg); + } + break; + } + default: + break; + } +} + + +/********************************************************************** + * re_scale_and_move_bln_word() + * + * Scale and move a bln word so that it fits in a specified bounding box. + * Scale by width or height to generate the largest image + **********************************************************************/ + +float re_scale_and_move_bln_word( //put bln word in box + WERD *norm_word, //BL normalised word + const BOX &box //destination box + ) { + BOX norm_box = norm_word->bounding_box (); + float width_scale_factor; + float height_scale_factor; + float selected_scale_factor; + + width_scale_factor = box.width () / (float) norm_box.width (); + height_scale_factor = box.height () / (float) ASC_HEIGHT; + + if ((ASC_HEIGHT * width_scale_factor) <= box.height ()) + selected_scale_factor = width_scale_factor; + else + selected_scale_factor = height_scale_factor; + + norm_word->scale (selected_scale_factor); + norm_word->move (ICOORD ((box.left () + box.width () / 2), box.bottom ())); + return selected_scale_factor; +} + + +/********************************************************************** + * re_segment_word() + * + * If all selected blobs are in the same row, remove them from their current + * word(s) and put them in a new word. Insert the new word in the row at the + * appropriate point. Delete any empty words. + * + **********************************************************************/ + +void re_segment_word( //break/join words + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box) { + BLOCK_IT block_it(block_list); + BLOCK *block; + BLOCK *block_to_process = NULL; + ROW_IT row_it; + ROW *row; + ROW *row_to_process = NULL; + WERD_IT word_it; + WERD *word; + WERD *new_word = NULL; + BOOL8 polyg = false; + PBLOB_IT blob_it; + PBLOB_LIST dummy; // Just to initialize new_blob_it. + PBLOB_IT new_blob_it = &dummy; + PBLOB *blob; + + /* Find row to process - error if selections from more than one row */ + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + if (row_to_process == NULL) { + block_to_process = block; + row_to_process = row; + } + else { + command_window-> + msg ("Cant resegment words in more than one row"); + return; + } + } + } + } + } + /* Continue with row_to_process */ + + word_it.set_to_list (row_to_process->word_list ()); + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + polyg = word->flag (W_POLYGON); + if (word->bounding_box ().overlap (selection_box)) { + blob_it.set_to_list (word->gblob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (gblob_bounding_box (blob, polyg).overlap (selection_box)) { + if (new_word == NULL) { + new_word = word->shallow_copy (); + new_blob_it.set_to_list (new_word->gblob_list ()); + } + new_blob_it.add_to_end (blob_it.extract ()); + //move blob + } + } + if (blob_it.empty ()) { //no blobs in word + //so delete word + delete word_it.extract (); + } + } + } + if (new_word != NULL) { + gblob_sort_list (new_word->gblob_list (), polyg); + word_it.add_to_end (new_word); + word_it.sort (word_comparator); + row_to_process->bounding_box ().plot (image_win, + INT_SOLID, FALSE, BLACK, BLACK); + word_it.set_to_list (row_to_process->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) + word_display (block_to_process, row_to_process, word_it.data ()); + *current_image_changed = TRUE; + } +} + + +void block_space_stat( //show space stats + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + int block_idx = 0; + STATS all_gap_stats (0, MAXSPACING); + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + C_BLOB_IT cblob_it; + C_BLOB *cblob; + BOX box; + INT16 prev_box_right; + INT16 gap_width; + INT16 min_inter_word_gap; + INT16 max_inter_char_gap; + + /* Find blocks to process */ + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block_idx++; + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + /* Process a block */ + tprintf ("\nBlock %d\n", block_idx); + min_inter_word_gap = 3000; + max_inter_char_gap = 0; + all_gap_stats.clear (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + prev_box_right = -1; + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->flag (W_POLYGON)) { + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + box = blob->bounding_box (); + if (prev_box_right > -1) { + gap_width = box.left () - prev_box_right; + all_gap_stats.add (gap_width, 1); + if (blob_it.at_first ()) { + if (gap_width < min_inter_word_gap) + min_inter_word_gap = gap_width; + } + else { + if (gap_width > max_inter_char_gap) + max_inter_char_gap = gap_width; + } + } + prev_box_right = box.right (); + } + } + else { + cblob_it.set_to_list (word->cblob_list ()); + for (cblob_it.mark_cycle_pt (); + !cblob_it.cycled_list (); cblob_it.forward ()) { + cblob = cblob_it.data (); + box = cblob->bounding_box (); + if (prev_box_right > -1) { + gap_width = box.left () - prev_box_right; + all_gap_stats.add (gap_width, 1); + if (cblob_it.at_first ()) { + if (gap_width < min_inter_word_gap) + min_inter_word_gap = gap_width; + } + else { + if (gap_width > max_inter_char_gap) + max_inter_char_gap = gap_width; + } + } + prev_box_right = box.right (); + } + } + } + } + tprintf ("Max inter char gap = %d.\nMin inter word gap = %d.\n", + max_inter_char_gap, min_inter_word_gap); + all_gap_stats.short_print (NULL, TRUE); + all_gap_stats.smooth (2); + tprintf ("SMOOTHED DATA...\n"); + all_gap_stats.short_print (NULL, TRUE); + } + } +} + + +void row_space_stat( //show space stats + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + int block_idx = 0; + int row_idx; + STATS all_gap_stats (0, MAXSPACING); + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + C_BLOB_IT cblob_it; + C_BLOB *cblob; + BOX box; + INT16 prev_box_right; + INT16 gap_width; + INT16 min_inter_word_gap; + INT16 max_inter_char_gap; + + /* Find rows to process */ + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block_idx++; + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + row_idx = 0; + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row_idx++; + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + /* Process a row */ + + tprintf ("\nBlock %d Row %d\n", block_idx, row_idx); + min_inter_word_gap = 3000; + max_inter_char_gap = 0; + prev_box_right = -1; + all_gap_stats.clear (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->flag (W_POLYGON)) { + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + box = blob->bounding_box (); + if (prev_box_right > -1) { + gap_width = box.left () - prev_box_right; + all_gap_stats.add (gap_width, 1); + if (blob_it.at_first ()) { + if (gap_width < min_inter_word_gap) + min_inter_word_gap = gap_width; + } + else { + if (gap_width > max_inter_char_gap) + max_inter_char_gap = gap_width; + } + } + prev_box_right = box.right (); + } + } + else { + cblob_it.set_to_list (word->cblob_list ()); + for (cblob_it.mark_cycle_pt (); + !cblob_it.cycled_list (); cblob_it.forward ()) { + cblob = cblob_it.data (); + box = cblob->bounding_box (); + if (prev_box_right > -1) { + gap_width = box.left () - prev_box_right; + all_gap_stats.add (gap_width, 1); + if (cblob_it.at_first ()) { + if (gap_width < min_inter_word_gap) + min_inter_word_gap = gap_width; + } + else { + if (gap_width > max_inter_char_gap) + max_inter_char_gap = gap_width; + } + } + prev_box_right = box.right (); + } + } + } + tprintf + ("Max inter char gap = %d.\nMin inter word gap = %d.\n", + max_inter_char_gap, min_inter_word_gap); + all_gap_stats.short_print (NULL, TRUE); + all_gap_stats.smooth (2); + tprintf ("SMOOTHED DATA...\n"); + all_gap_stats.short_print (NULL, TRUE); + } + } + } + } +} + + +/********************************************************************** + * show_point() + * + * Show coords of point, blob bounding box, word bounding box and offset from + * row baseline + **********************************************************************/ + +void show_point( //display posn of bloba word + BLOCK_LIST *block_list, //blocks to check + float x, + float y) { + FCOORD pt(x, y); + BOX box; + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + C_BLOB_IT cblob_it; + C_BLOB *cblob; + + char msg[160]; + char *msg_ptr = msg; + + msg_ptr += sprintf (msg_ptr, "Pt:(%0.3f, %0.3f) ", x, y); + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().contains (pt)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().contains (pt)) { + msg_ptr += sprintf (msg_ptr, "BL(x)=%0.3f ", + row->base_line (x)); + + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + box = word->bounding_box (); + if (box.contains (pt)) { + msg_ptr += sprintf (msg_ptr, + "Wd(%d, %d)/(%d, %d) ", + box.left (), box.bottom (), + box.right (), box.top ()); + + if (word->flag (W_POLYGON)) { + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + box = blob->bounding_box (); + if (box.contains (pt)) { + msg_ptr += sprintf (msg_ptr, + "Blb(%d, %d)/(%d, %d) ", + box.left (), + box.bottom (), + box.right (), + box.top ()); + } + } + } + else { + cblob_it.set_to_list (word->cblob_list ()); + for (cblob_it.mark_cycle_pt (); + !cblob_it.cycled_list (); + cblob_it.forward ()) { + cblob = cblob_it.data (); + box = cblob->bounding_box (); + if (box.contains (pt)) { + msg_ptr += sprintf (msg_ptr, + "CBlb(%d, %d)/(%d, %d) ", + box.left (), + box.bottom (), + box.right (), + box.top ()); + } + } + } + } + } + } + } + } + } + command_window->msg (msg); +} + + +/********************************************************************** + * WERD PROCESSOR FUNCTIONS + * ======================== + * + * These routines are invoked by one or mode of: + * process_all_words() + * process_selected_words() + * or + * process_all_words_it() + * process_selected_words_it() + * for each word to be processed + **********************************************************************/ + +/********************************************************************** + * word_blank_and_set_display() Word processor + * + * Blank display of word then redisplay word according to current display mode + * settings + **********************************************************************/ + +BOOL8 word_blank_and_set_display( //display a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + word->bounding_box ().plot (image_win, INT_SOLID, FALSE, BLACK, BLACK); + return word_set_display (block, row, word); +} + + +/********************************************************************** + * word_bln_display() + * + * Normalise word and display in word window + **********************************************************************/ + +BOOL8 word_bln_display( //bln & display + BLOCK *, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + WERD *bln_word; + + bln_word = word->poly_copy (row->x_height ()); + bln_word->baseline_normalise (row); + clear_view_surface (bln_word_window_handle ()); + display_bln_lines (bln_word_window_handle (), CYAN, 1.0, 0.0f, -1000.0f, + 1000.0f); + bln_word->plot (bln_word_window_handle (), RED); + delete bln_word; + return TRUE; +} + + +/********************************************************************** + * word_change_text() + * + * Change the correct text of a word + **********************************************************************/ + +BOOL8 word_change_text( //change correct text + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + char response_str[MAX_CHARS + 1]; + + strcpy (response_str, word->text ()); + if (!command_window-> + prompt ("Enter/edit the correct text and press <>", + response_str)) + return FALSE; + else + word->set_text (response_str); + + if (word_display_mode.bit (DF_TEXT) || word->display_flag (DF_TEXT)) { + word_blank_and_set_display(block, row, word); + overlap_picture_ops(TRUE); + } + + *current_image_changed = TRUE; + return TRUE; +} + + +/********************************************************************** + * word_copy() + * + * Copy a word to other display list + **********************************************************************/ + +BOOL8 word_copy( //copy a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + WERD *copy_word = new WERD; + + *copy_word = *word; + add_word(copy_word, row, block, other_block_list); + *other_image_changed = TRUE; + return TRUE; +} + + +/********************************************************************** + * word_delete() + * + * Delete a word + **********************************************************************/ + +BOOL8 word_delete( //delete a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word, //word to be processed + BLOCK_IT &block_it, //block list iterator + ROW_IT &row_it, //row list iterator + WERD_IT &word_it //word list iterator + ) { + word_it.extract (); + word->bounding_box ().plot (image_win, INT_SOLID, FALSE, BLACK, BLACK); + delete(word); + + if (word_it.empty ()) { //no words left in row + //so delete row + row_it.extract (); + row->bounding_box ().plot (image_win, INT_SOLID, FALSE, BLACK, BLACK); + delete(row); + + if (row_it.empty ()) { //no rows left in blk + //so delete block + block_it.extract (); + block->bounding_box ().plot (image_win, INT_SOLID, FALSE, + BLACK, BLACK); + delete(block); + } + } + *current_image_changed = TRUE; + return TRUE; +} + + +/********************************************************************** + * word_display() Word Processor + * + * Display a word according to its display modes + **********************************************************************/ + +BOOL8 word_display( // display a word + BLOCK *, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + BOX word_bb; //word bounding box + int word_height; //ht of word BB + BOOL8 displayed_something = FALSE; + BOOL8 displayed_rainbow = FALSE; + float shift; //from bot left + PBLOB_IT it; //blob iterator + C_BLOB_IT c_it; //cblob iterator + WERD *word_ptr; //poly copy + WERD temp_word; + float scale_factor; //for BN_POLYGON + + /* + Note the double coercions of (COLOUR)((INT32)editor_image_word_bb_color) + etc. are to keep the compiler happy. + */ + + //display bounding box + if (word->display_flag (DF_BOX)) { + word->bounding_box ().plot (image_win, INT_HOLLOW, TRUE, + (COLOUR) ((INT32) + editor_image_word_bb_color), + (COLOUR) ((INT32) + editor_image_word_bb_color)); + + perimeter_color_index (image_win, + (COLOUR) ((INT32) editor_image_blob_bb_color)); + if (word->flag (W_POLYGON)) { + it.set_to_list (word->blob_list ()); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->bounding_box ().plot (image_win); + } + else { + c_it.set_to_list (word->cblob_list ()); + for (c_it.mark_cycle_pt (); !c_it.cycled_list (); c_it.forward ()) + c_it.data ()->bounding_box ().plot (image_win); + } + displayed_something = TRUE; + } + + //display edge steps + if (word->display_flag (DF_EDGE_STEP) && + !word->flag (W_POLYGON)) { //edgesteps available + word->plot (image_win); //rainbow colors + displayed_something = TRUE; + displayed_rainbow = TRUE; + } + + //display poly approx + if (word->display_flag (DF_POLYGONAL)) { + //need to convert + if (!word->flag (W_POLYGON)) { + word_ptr = word->poly_copy (row->x_height ()); + + /* CALL POLYGONAL APPROXIMATOR WHEN AVAILABLE - on a temp_word */ + + if (displayed_rainbow) + //ensure its visible + word_ptr->plot (image_win, WHITE); + else + //rainbow colors + word_ptr->plot (image_win); + delete word_ptr; + } + else { + if (displayed_rainbow) + //ensure its visible + word->plot (image_win, WHITE); + else + word->plot (image_win); //rainbow colors + } + + displayed_rainbow = TRUE; + displayed_something = TRUE; + } + + //disp BN poly approx + if (word->display_flag (DF_BN_POLYGONAL)) { + //need to convert + if (!word->flag (W_POLYGON)) { + word_ptr = word->poly_copy (row->x_height ()); + temp_word = *word_ptr; + delete word_ptr; + + /* CALL POLYGONAL APPROXIMATOR WHEN AVAILABLE - on a temp_word */ + + } + else + temp_word = *word; //copy word + word_bb = word->bounding_box (); + if (!temp_word.flag (W_NORMALIZED)) + temp_word.baseline_normalise (row); + + scale_factor = re_scale_and_move_bln_word (&temp_word, word_bb); + display_bln_lines (image_win, CYAN, scale_factor, word_bb.bottom (), + word_bb.left (), word_bb.right ()); + + if (displayed_rainbow) + //ensure its visible + temp_word.plot (image_win, WHITE); + else + temp_word.plot (image_win);//rainbow colors + + displayed_rainbow = TRUE; + displayed_something = TRUE; + } + + //display correct text + if (word->display_flag (DF_TEXT)) { + word_bb = word->bounding_box (); + text_color_index (image_win, + (COLOUR) ((INT32) editor_image_text_color)); + text_font_index (image_win, 1); + word_height = word_bb.height (); + character_height (image_win, 0.75 * word_height); + + if (word_height < word_bb.width ()) + shift = 0.25 * word_height; + else + shift = 0.0f; + + text2d (image_win, + word_bb.left () + shift, + word_bb.bottom () + 0.25 * word_height, + word->text (), 0, FALSE); + if (strlen (word->text ()) > 0) + displayed_something = TRUE; + } + + if (!displayed_something) //display BBox anyway + word->bounding_box ().plot (image_win, INT_HOLLOW, TRUE, + (COLOUR) ((INT32) editor_image_word_bb_color), + (COLOUR) ((INT32) + editor_image_word_bb_color)); + return TRUE; +} + + +/********************************************************************** + * word_dumper() + * + * Dump members to the debug window + **********************************************************************/ + +BOOL8 word_dumper( //dump word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + + tprintf ("\nBlock data...\n"); + block->print (NULL, FALSE); + tprintf ("\nRow data...\n"); + row->print (NULL); + tprintf ("\nWord data...\n"); + word->print (NULL); + return TRUE; +} + + +/********************************************************************** + * word_set_display() Word processor + * + * Display word according to current display mode settings + **********************************************************************/ + +BOOL8 word_set_display( //display a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + BOX word_bb; //word bounding box + + word->set_display_flag (DF_BOX, word_display_mode.bit (DF_BOX)); + word->set_display_flag (DF_TEXT, word_display_mode.bit (DF_TEXT)); + word->set_display_flag (DF_POLYGONAL, word_display_mode.bit (DF_POLYGONAL)); + word->set_display_flag (DF_EDGE_STEP, word_display_mode.bit (DF_EDGE_STEP)); + word->set_display_flag (DF_BN_POLYGONAL, + word_display_mode.bit (DF_BN_POLYGONAL)); + *current_image_changed = TRUE; + return word_display (block, row, word); +} + + +/********************************************************************** + * word_toggle_seg() + * + * Toggle the correct segmentation flag + **********************************************************************/ + +BOOL8 word_toggle_seg( //toggle seg flag + BLOCK *, //block holding word + ROW *, //row holding word + WERD *word //word to be processed + ) { + word->set_flag (W_SEGMENTED, !word->flag (W_SEGMENTED)); + *current_image_changed = TRUE; + return TRUE; +} + + +/* DEBUG ONLY */ + +void do_check_mem( //do it + INT32 level) { + check_mem ("Doing it", level); +} diff --git a/display/pgedit.h b/display/pgedit.h new file mode 100644 index 0000000000..b03c5dc6a9 --- /dev/null +++ b/display/pgedit.h @@ -0,0 +1,176 @@ +/********************************************************************** + * File: pgedit.h (Formerly pgeditor.h) + * Description: Page structure file editor + * Author: Phil Cheatle + * Created: Thu Oct 10 16:25:24 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef PGEDIT_H +#define PGEDIT_H + +#include "ocrblock.h" +#include "ocrrow.h" +#include "werd.h" +#include "rect.h" +#include "pagewalk.h" +//#include "basefile.h" +#include "evnts.h" +#include "sbdmenu.h" +#include "varable.h" +#include "notdll.h" + +extern WINDOW image_win; +extern COMMAND_WINDOW *command_window; +extern BLOCK_LIST *current_block_list; +extern BOOL8 *current_image_changed; +extern STRING_VAR_H (editor_image_win_name, "EditorImage", +"Editor image window name"); +extern INT_VAR_H (editor_image_xpos, 590, "Editor image X Pos"); +extern INT_VAR_H (editor_image_ypos, 10, "Editor image Y Pos"); +extern INT_VAR_H (editor_image_height, 680, "Editor image height"); +extern INT_VAR_H (editor_image_width, 655, "Editor image width"); +extern INT_VAR_H (editor_image_word_bb_color, BLUE, +"Word bounding box colour"); +extern INT_VAR_H (editor_image_blob_bb_color, YELLOW, +"Blob bounding box colour"); +extern INT_VAR_H (editor_image_text_color, WHITE, "Correct text colour"); +extern STRING_VAR_H (editor_dbwin_name, "EditorDBWin", +"Editor debug window name"); +extern INT_VAR_H (editor_dbwin_xpos, 50, "Editor debug window X Pos"); +extern INT_VAR_H (editor_dbwin_ypos, 500, "Editor debug window Y Pos"); +extern INT_VAR_H (editor_dbwin_height, 24, "Editor debug window height"); +extern INT_VAR_H (editor_dbwin_width, 80, "Editor debug window width"); +extern STRING_VAR_H (editor_word_name, "BlnWords", +"BL normalised word window"); +extern INT_VAR_H (editor_word_xpos, 60, "Word window X Pos"); +extern INT_VAR_H (editor_word_ypos, 510, "Word window Y Pos"); +extern INT_VAR_H (editor_word_height, 240, "Word window height"); +extern INT_VAR_H (editor_word_width, 655, "Word window width"); +extern double_VAR_H (editor_smd_scale_factor, 1.0, "Scaling for smd image"); +void add_word( //to block list + WERD *word, //word to be added + ROW *src_row, //source row + BLOCK *src_block, //source block + BLOCK_LIST *dest_block_list //add to this + ); +WINDOW bln_word_window_handle(); //return handle +void build_image_window(BOX page_bounding_box); +MENU_ROOT *build_menu(); +void debug_window_handle(); //return handle +void display_bln_lines(WINDOW window, + COLOUR colour, + float scale_factor, + float y_offset, + float minx, + float maxx); +void do_new_source( //serialise + char *name //file name + ); + //function to call +void do_re_display (BOOL8 word_painter ( +BLOCK *, ROW *, WERD *)); +const BOX do_tidy_cmd(); //tidy +void do_view_cmd(); +void do_write_file( //serialise + char *name //file name + ); +void smd_cmd(); +void pgeditor_main(BLOCK_LIST *blocks); +void pgeditor_msg( //message display + const char *msg); + //of serialised file +void pgeditor_read_file(STRING &name, + BLOCK_LIST *blocks //block list to add to + ); +void pgeditor_show_point( //display coords + GRAPHICS_EVENT *event); +void pgeditor_write_file( //serialise + char *name, //file name + BLOCK_LIST *blocks //block list to write + ); +BOOL8 process_cmd_win_event( //UI command semantics + INT32 cmd_event, //which menu item? + char *new_value //any prompt data + ); +void process_image_event( //action in image win + GRAPHICS_EVENT event); + //put bln word in box +float re_scale_and_move_bln_word(WERD *norm_word, //BL normalised word + const BOX &box //destination box + ); +void re_segment_word( //break/join words + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box); +void block_space_stat( //show space stats + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box); +void row_space_stat( //show space stats + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box); +void show_point( //display posn of bloba word + BLOCK_LIST *block_list, //blocks to check + float x, + float y); + //display a word +BOOL8 word_blank_and_set_display(BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_bln_display( //bln & display + BLOCK *, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_change_text( //change correct text + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_copy( //copy a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_delete( //delete a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word, //word to be processed + BLOCK_IT &block_it, //block list iterator + ROW_IT &row_it, //row list iterator + WERD_IT &word_it //word list iterator + ); +BOOL8 word_display( // display a word + BLOCK *, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_dumper( //dump word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_set_display( //display a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_toggle_seg( //toggle seg flag + BLOCK *, //block holding word + ROW *, //row holding word + WERD *word //word to be processed + ); +void do_check_mem( //do it + INT32 level); +#endif diff --git a/display/pgeditx.h b/display/pgeditx.h new file mode 100644 index 0000000000..4d1f52d0fb --- /dev/null +++ b/display/pgeditx.h @@ -0,0 +1,20 @@ +/************************************************************************* + * The following functions are the standard external calls that pgeditor makes + * to its client programs. They are included explicitly here to avoid making + * pgeditor.c dependent on any/all of its clients + ***************************************************************************/ + + //handle for "MODES" +void extend_menu(RADIO_MENU *modes_menu, + INT16 modes_id_base, //mode cmd ids offset + NON_RADIO_MENU *other_menu, //handle for "OTHER" + INT16 other_id_base //mode cmd ids offset + ); + //current mode +void extend_moded_commands(INT32 mode, + BOX selection_box //area selected + ); + //current mode +void extend_unmoded_commands(INT32 cmd_event, + char *new_value //changed value if any + ); diff --git a/display/sbdmenu.cpp b/display/sbdmenu.cpp new file mode 100644 index 0000000000..15bcac87cb --- /dev/null +++ b/display/sbdmenu.cpp @@ -0,0 +1,520 @@ +/********************************************************************** + * File: sbdmenu.cpp (Formerly menu.c) + * Description: Command Window MENU class + * Author: Phil Cheatle + * Created: Thu Oct 10 16:25:24 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "sbdmenu.h" + +#define MENU_COLUMN_SPACING (menu_char_height / 2) +#define MENU_ITEM_HEIGHT (int)(menu_char_height * 1.5) +#define MENU_SEPARATOR_HEIGHT MENU_COLUMN_SPACING +#define MENU_TEXT_X_OFFSET (menu_char_height / 2) +#define MENU_TEXT_Y_OFFSET menu_char_height/2 + +#include "notdll.h" +ELISTIZE(MENU_L); + +const ERRCODE NOT_SUBMENU_NODE = "Button is not leaf of radio submenu"; +const ERRCODE SHOULDNT_BE_CALLED = "Should never get to here!!"; + +INT_VAR (menu_char_width, 8, "Width of characters in menu text"); +INT_VAR (menu_char_height, 14, "Height characters in menu text"); + +/********************************************************************** + *********************************************************************** + * + * LEAF_MENU_NODE MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * LEAF_MENU_NODE::plotx( ) + * + * The real plot. Positions assumed already calculated. + **********************************************************************/ + +void LEAF_MENU_NODE::plotx( //draw it + WINDOW window //in this window + ) { + box.plot (window); + + text2d (window, + box.left () + MENU_TEXT_X_OFFSET, + box.top () - MENU_TEXT_Y_OFFSET, name.string (), 0, FALSE); +} + + +/********************************************************************** + *********************************************************************** + * + * MENU_NODE MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * MENU_NODE::plot( ) + * + * Initialise, then call plotx to do the real work + * (This means that we can initialise plotting once only REGARDLESS of what menu + * subclass we start plotting. (EG - we don't always start with a MENU_ROOT.) + **********************************************************************/ + +void MENU_NODE::plot( //draw it + WINDOW window //window to draw in + ) { + character_height(window, menu_char_height); + interior_style(window, INT_SOLID, TRUE); + perimeter_color_index(window, LIGHT_GREY); + text_color_index(window, WHITE); + fill_color_index(window, GREY); + plotx(window); +} + + +/********************************************************************** + * MENU_NODE::recalc_bounding_box( ) + * + * SHOULD NEVER BE CALLED. Compiler needs it as this can't be a pure virtual + * function as not all classes provide it and the compiler will not let you do + * "new" on a class which inherits a pure virtual function which it cannot + * resolve into a real function. + **********************************************************************/ + +BOX &MENU_NODE::recalc_bounding_box( // calculate size + INT16, // start topleft x + INT16 // start topleft y + ) { + SHOULDNT_BE_CALLED.error ("MENU_ROOT::recalc_bounding_box", ABORT, NULL); + return box; +} + + +/********************************************************************** + * MENU_NODE::recalc_fixed_width_bb( ) + * + * Calculate the selectable area of the menu item and hence its display + * position. Note that the width is predetermined. + **********************************************************************/ + +BOX &MENU_NODE::recalc_fixed_width_bb( // calc BB + INT16 tl_x, // start topleft x + INT16 tl_y, // start topleft y + INT16 width // width + ) { + box = BOX (ICOORD (tl_x, tl_y - MENU_ITEM_HEIGHT), + ICOORD (tl_x + width, tl_y)); + return box; +} + + +/********************************************************************** + *********************************************************************** + * + * MENU_ROOT MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * MENU_ROOT::plotx( ) + * + * The real plot - only called internally after initialisation. + * Display the menu recursively. Positions assumed already calculated. + **********************************************************************/ + +void MENU_ROOT::plotx( //draw it + WINDOW window //in this window + ) { + MENU_L_IT it(&menu_list); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->ptr->plotx (window); +} + + +/********************************************************************** + * MENU_ROOT::recalc_bounding_box( ) + * + * Calculate the selectable areas of the menu items and hence their display + * positions. + **********************************************************************/ + +BOX &MENU_ROOT::recalc_bounding_box( // calculate size + INT16 tl_x, // start topleft x + INT16 tl_y // start topleft y + ) { + MENU_L_IT it(&menu_list); + + box = BOX (ICOORD (tl_x, tl_y), ICOORD (tl_x, tl_y)); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + box += it.data ()->ptr->recalc_bounding_box (box.right (), box.top ()); + box.move_right_edge (MENU_COLUMN_SPACING); + } + box.move_right_edge (-MENU_COLUMN_SPACING); + return box; +} + + +/********************************************************************** + * MENU_ROOT::write_vars( ) + * + * Just propogate down the menu tree + **********************************************************************/ + +void MENU_ROOT::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + MENU_L_IT it(&menu_list); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->ptr->write_vars (fp, changes_only); +} + + +/********************************************************************** + *********************************************************************** + * + * NON_LEAF_MENU_NODE MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * NON_LEAF_MENU_NODE::link_child( MENU_NODE* ) + * + * Add a child to the end of the menu list + **********************************************************************/ + +void NON_LEAF_MENU_NODE::link_child( //add to menu end + MENU_NODE *new_child) { + MENU_L_IT it(&menu_list); + + it.add_to_end (new MENU_L (new_child)); +} + + +/********************************************************************** + * NON_LEAF_MENU_NODE::link_child_link( ) + * + * Add a child to the end of the menu list + **********************************************************************/ + +void NON_LEAF_MENU_NODE::link_child_link( //add to menu + MENU_L *new_child) { + MENU_L_IT it(&menu_list); + + it.add_to_end (new_child); +} + + +/********************************************************************** + * NON_LEAF_MENU_NODE::event( ) + * + * Event selection + **********************************************************************/ + +void NON_LEAF_MENU_NODE::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value //Edited value + ) { + MENU_L_IT it(&menu_list); + + if (box.contains (pt)) + for (it.mark_cycle_pt (); + (*cmd_event_id == UNIDENTIFIED_COMMAND) && !it.cycled_list (); + it.forward ()) + it.data ()->ptr->event (cmd_win, pt, cmd_event_id, new_value); +} + + +/********************************************************************** + * NON_LEAF_MENU_NODE::max_num_chars( ) + * + * Calculate the max character width of any item of this and its children + **********************************************************************/ + +INT8 NON_LEAF_MENU_NODE::max_num_chars() { //calc char width + MENU_L_IT it(&menu_list); + INT8 max_so_far = 0; + INT8 current_char_count = 0; + + max_so_far = name.length (); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + current_char_count = it.data ()->ptr->max_num_chars (); + if (current_char_count > max_so_far) + max_so_far = current_char_count; + } + return max_so_far; +} + + +/********************************************************************** + * NON_LEAF_MENU_NODE::plotx( ) (Other than ROOT) + * + * The real plot - only called internally after initialisation. + * Display the menu recursively. Positions assumed already calculated. + **********************************************************************/ + +void NON_LEAF_MENU_NODE::plotx( //draw it + WINDOW window //in this window + ) { + MENU_L_IT it(&menu_list); + + box.plot (window); + text2d (window, + box.left () + MENU_TEXT_X_OFFSET, + box.top () - MENU_TEXT_Y_OFFSET, name.string (), 0, FALSE); + + move2d (window, + box.left (), + box.top () - MENU_ITEM_HEIGHT - MENU_SEPARATOR_HEIGHT / 2); + draw2d (window, + box.right (), + box.top () - MENU_ITEM_HEIGHT - MENU_SEPARATOR_HEIGHT / 2); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->ptr->plotx (window); +} + + +/********************************************************************** + * NON_LEAF_MENU_NODE::recalc_bounding_box( ) + * + * Calculate the selectable areas of the menu items and hence their display + * positions. + **********************************************************************/ + +BOX &NON_LEAF_MENU_NODE::recalc_bounding_box( // calc BB + INT16 tl_x, // start topleft x + INT16 tl_y // start topleft y + ) { + MENU_L_IT it(&menu_list); + INT16 + menu_width = max_num_chars () * menu_char_width + 2 * MENU_TEXT_X_OFFSET; + + box = BOX (ICOORD (tl_x, tl_y - MENU_ITEM_HEIGHT - MENU_SEPARATOR_HEIGHT), + ICOORD (tl_x + menu_width, tl_y)); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + box += it.data ()->ptr->recalc_fixed_width_bb (box.left (), + box.bottom (), + box.width ()); + } + return box; +} + + +/********************************************************************** + *********************************************************************** + * + * RADIO_MENU MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * RADIO_MENU::add_child( ) + * + * Add a radio button + **********************************************************************/ + +void RADIO_MENU::add_child( //Add button + RADIO_MENU_LEAF *new_child) { //item to add + if (menu_list.empty ()) { //Init 1st choice ON + on_button = new_child; + new_child->state = TRUE; + } + link_child(new_child); +} + + +/********************************************************************** + * RADIO_MENU::event( ) + * + * Event selection + **********************************************************************/ + +void RADIO_MENU::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value //Edited value + ) { + MENU_L_IT it(&menu_list); + + if (box.contains (pt)) { + if (!on_button->box.contains (pt)) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->ptr->event (cmd_win, pt, cmd_event_id, new_value); + if (*cmd_event_id != UNIDENTIFIED_COMMAND) { + on_button->state = FALSE; + on_button = (RADIO_MENU_LEAF *) it.data ()->ptr; + break; + } + } + } + } +} + + +/********************************************************************** + * RADIO_MENU::press_radio_button() + * + * Change the selected button in a radio menu + **********************************************************************/ + +void RADIO_MENU::press_radio_button( //Chnge selctd butn + RADIO_MENU_LEAF *button //to this one + ) { + MENU_L_IT it(&menu_list); + + if (button != on_button) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + if (it.data ()->ptr == button) + break; + } + + if (it.cycled_list ()) //couldnt find it + NOT_SUBMENU_NODE.error ("MENU::press_radio_button", ABORT, NULL); + + on_button->state = FALSE; + button->state = TRUE; + on_button = button; + } +} + + +/********************************************************************** + *********************************************************************** + * + * SIMPLE_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * SIMPLE_MENU_LEAF::event( ) + * + * Event selection + **********************************************************************/ + +void SIMPLE_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value //Edited value + ) { + if (box.contains (pt)) { + *cmd_event_id = event_id; + new_value[0] = '\0'; + } +} + + +/********************************************************************** + *********************************************************************** + * + * TOGGLE_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * TOGGLE_MENU_LEAF::event( ) + * + * Event selection + **********************************************************************/ + +void TOGGLE_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value //Edited value + ) { + if (box.contains (pt)) { + state = !state; + *cmd_event_id = event_id; + strcpy (new_value, state ? "T" : "F"); + } +} + + +/********************************************************************** + * TOGGLE_MENU_LEAF::plotx( ) + * + * The real plot. Positions assumed already calculated. + **********************************************************************/ + +void TOGGLE_MENU_LEAF::plotx( //draw it + WINDOW window //in this window + ) { + if (state) { + text_color_index(window, BLACK); + fill_color_index(window, WHITE); + } + else { + text_color_index(window, WHITE); + fill_color_index(window, GREY); + } + + LEAF_MENU_NODE::plotx(window); + + text_color_index(window, WHITE); + fill_color_index(window, GREY); +} + + +/********************************************************************** + *********************************************************************** + * + * VARIABLE_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * VARIABLE_MENU_LEAF::event( ) + * + * Event selection + **********************************************************************/ + +void VARIABLE_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value //Edited value + ) { + char prompt_msg[MAX_CHARS + 1]; + + if (box.contains (pt)) { + strcpy (new_value, current_value.string ()); + sprintf (prompt_msg, "New value for " "%s" "?", name.string ()); + if (cmd_win->internal_prompt (prompt_msg, new_value)) { + current_value = new_value; + *cmd_event_id = event_id; + } + else + *cmd_event_id = NULL_COMMAND; + } +} diff --git a/display/sbdmenu.h b/display/sbdmenu.h new file mode 100644 index 0000000000..e79f1d01a5 --- /dev/null +++ b/display/sbdmenu.h @@ -0,0 +1,415 @@ +/********************************************************************** + * File: sbdmenu.h (Formerly menu.h) + * Description: Command Window MENU class + * Author: Phil Cheatle + * Created: Thu Oct 10 16:25:24 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef SBDMENU_H +#define SBDMENU_H + +#include "hosthplb.h" +#include "elst.h" +#include "strngs.h" +#include "grphics.h" +#include "varable.h" +#include "rect.h" +#include "points.h" +#include "notdll.h" + +extern INT_VAR_H (menu_char_width, 8, "Width of characters in menu text"); +extern INT_VAR_H (menu_char_height, 14, "Height characters in menu text"); + +class MENU_L; //Forward Declaration +ELISTIZEH(MENU_L); + +class COMMAND_WINDOW; //Fwd Decl #inc at end + +/* Forward declarations showing the inheritance tree of the MENU_NODE + subclasses */ + +class MENU_NODE; +class LEAF_MENU_NODE; +class SIMPLE_MENU_LEAF; +class TOGGLE_MENU_LEAF; +class RADIO_MENU_LEAF; +class VARIABLE_MENU_LEAF; +class NON_LEAF_MENU_NODE; +class MENU_ROOT; +class NON_RADIO_MENU; +class RADIO_MENU; + +/********************************************************************** + +Class MENU_NODE - Generic menu item - an abstract class. + +**********************************************************************/ + +class MENU_NODE +{ + friend class MENU_ROOT; + friend class NON_LEAF_MENU_NODE; + friend class LEAF_MENU_NODE; + friend class RADIO_MENU; + protected: + STRING name; //Text of option + BOX box; //Display area + + MENU_NODE( //constructor + const char *txt) { + name = STRING (txt); + } + + virtual void event ( //User clicked... + COMMAND_WINDOW * cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 * cmd_event_id, //Command selected + char *new_value) = 0; //Edited value + + //char width reqd + virtual INT8 max_num_chars () = 0; + + virtual void plotx ( //draw it + WINDOW window) = 0; //in this window + + //build box + virtual BOX &recalc_bounding_box(INT16 tl_x, //top left x + INT16 tl_y); //top left y + + private: + BOX &recalc_fixed_width_bb( //build box + INT16 tl_x, //top left x + INT16 tl_y, //top left y + INT16 width); //required width + + public: + //return ptr to name + virtual const char *cmp_str() { + return name.string (); + } + + void plot( //draw it + WINDOW window); //in this window + + void new_label( //replace name + const char *label) { + name = label; + } + + virtual void write_vars( //save config vars + FILE *, //in this file + BOOL8) { //Changed vars only? + //default do nothing + } +}; + +/********************************************************************** + +Class LEAF_MENU_NODE - Generic menu leaf + +**********************************************************************/ + +class LEAF_MENU_NODE:public MENU_NODE +{ + protected: + LEAF_MENU_NODE ( //constructor + const char *menu_text):MENU_NODE (menu_text) { + } + + INT8 max_num_chars() { //char width reqd + return (INT8) name.length (); + } + + virtual void plotx( //draw it + WINDOW window); //in this window +}; + +/********************************************************************** + +Class SIMPLE_MENU_LEAF - Just returns its event code when selected + +**********************************************************************/ + +class SIMPLE_MENU_LEAF:public LEAF_MENU_NODE +{ + INT32 event_id; //Return event code + + private: + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + public: + SIMPLE_MENU_LEAF ( //constructor + const char *menu_text, + INT32 code):LEAF_MENU_NODE (menu_text) { + event_id = code; + } +}; + +/********************************************************************** + +Class TOGGLE_MENU_LEAF - Boolean toggle leaf + +**********************************************************************/ + +class TOGGLE_MENU_LEAF:public LEAF_MENU_NODE +{ + friend class RADIO_MENU; + + INT32 event_id; //Return event code + BOOL8 state; //ON/OFF + + private: + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + void plotx( //draw it + WINDOW window); //in this window + + public: + TOGGLE_MENU_LEAF ( //constructor + const char *menu_text, + INT32 code, + BOOL8 initial_state):LEAF_MENU_NODE (menu_text) { + event_id = code; + state = initial_state; + } + + void set_toggle( //Explicit override + BOOL8 value) { + state = value; + } +}; + +/********************************************************************** + +Class RADIO_MENU_LEAF - Press the radio button and return the event code +on selection + +**********************************************************************/ + +class RADIO_MENU_LEAF:public TOGGLE_MENU_LEAF +{ + public: + RADIO_MENU_LEAF ( //constructor + const char *menu_text, + INT32 code):TOGGLE_MENU_LEAF (menu_text, code, FALSE) { + } +}; + +/********************************************************************** + +Class VARIABLE_MENU_LEAF - Prompt for new value and return it + +**********************************************************************/ + +class VARIABLE_MENU_LEAF:public LEAF_MENU_NODE +{ + INT32 event_id; //Return event code + STRING current_value; //Variable value + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + public: + VARIABLE_MENU_LEAF ( //constructor + const char *menu_text, + INT32 code, + const char *initial_value):LEAF_MENU_NODE (menu_text) { + event_id = code; + current_value = STRING (initial_value); + } + + void replace_value( //change current_value + const char *new_value) { + current_value = new_value; + } +}; + +/********************************************************************** + +Class NON_LEAF_MENU_NODE - Generic menu item - an abstract class. + +**********************************************************************/ + +class NON_LEAF_MENU_NODE:public MENU_NODE +{ + public: + void clear_children() { + menu_list.clear (); + }; + + protected: + MENU_L_LIST menu_list; + + NON_LEAF_MENU_NODE ( //constructor + const char *menu_text):MENU_NODE (menu_text) { + } + + void link_child( //add to sub-menu end + MENU_NODE *new_child); //item to add + + void link_child_link( //add to sub-menu end + MENU_L *new_child); //item to add + + virtual void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + INT8 max_num_chars(); //char width reqd + + virtual void plotx( //draw it + WINDOW window); //in this window + + //build box + virtual BOX &recalc_bounding_box(INT16 tl_x, //top left x + INT16 tl_y); //top left y + +}; + +/********************************************************************** + +Class MENU_ROOT - The root of a menu tree + +**********************************************************************/ + +class MENU_ROOT:public NON_LEAF_MENU_NODE +{ + private: + void plotx( //draw it + WINDOW window); //in this window + + public: + MENU_ROOT ():NON_LEAF_MENU_NODE ("") { + } + //cnstrctr + + void add_child( //add to sub-menu end //item to add + NON_LEAF_MENU_NODE *new_child) { + link_child(new_child); + } + + /* Public defn of event - at menu_root only*/ + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value) { //Edited value + NON_LEAF_MENU_NODE::event(cmd_win, pt, cmd_event_id, new_value); + } + + BOX &recalc_bounding_box( //build box + INT16 tl_x, //top left x + INT16 tl_y); //top left y + + /* Public defn of write_vars - at menu_root only*/ + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? +}; + +/********************************************************************** + +Class NON_RADIO_MENU - The root of a menu tree + +**********************************************************************/ + +class NON_RADIO_MENU:public NON_LEAF_MENU_NODE +{ + public: + NON_RADIO_MENU ( //constructor + const char *menu_text):NON_LEAF_MENU_NODE (menu_text) { + } + + void add_child( //add to sub-menu end //item to add + SIMPLE_MENU_LEAF *new_child) { + link_child(new_child); + } + + void add_child( //add to sub-menu end //item to add + TOGGLE_MENU_LEAF *new_child) { + link_child(new_child); + } + + void add_child( //add to sub-menu end //item to add + VARIABLE_MENU_LEAF *new_child) { + link_child(new_child); + } + +}; + +/********************************************************************** + +Class RADIO_MENU - The root of a radio sub menu + +**********************************************************************/ + +class RADIO_MENU:public NON_LEAF_MENU_NODE +{ + private: + RADIO_MENU_LEAF * on_button; //which one is on? + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + public: + RADIO_MENU ( //constructor + const char *menu_text):NON_LEAF_MENU_NODE (menu_text) { + } + + void add_child( //add to sub-menu end + RADIO_MENU_LEAF *new_child); //item to add + + void press_radio_button( //Change selected butn + RADIO_MENU_LEAF *button); //to this one +}; + +/********************************************************************** + +Class MENU_L - Generic menu list element for heterogeneous list. + +**********************************************************************/ + +class MENU_L:public ELIST_LINK +{ + public: + MENU_NODE * ptr; //Generic menu item + + MENU_L() { + } //copy list constrctr + + MENU_L( //normal constructor + MENU_NODE *p) { + ptr = p; + } +}; + +#include "cmndwin.h" +#include "notdll.h" +#endif diff --git a/display/submen.h b/display/submen.h new file mode 100644 index 0000000000..40eadf8e2c --- /dev/null +++ b/display/submen.h @@ -0,0 +1,62 @@ +/********************************************************************** + * File: submen.h (Formerly submenu.h) + * Description: Variables submenu subclasses + * Author: Phil Cheatle + * Created: Wed Nov 13 11:17:26 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef SUBMEN_H +#define SUBMEN_H + +/********************************************************************** + +Class VAR_SUB_MENU - A leaf in the top level variable editor window + +**********************************************************************/ + +class VAR_SUB_MENU:public LEAF_MENU_NODE +{ + private: + MENU_ROOT * root; //Sub window menu + COMMAND_WINDOW *sub_window; //Sub window window + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only) { //Changed vars only? + fprintf (fp, "\n"); + //propogate to submenu + root->write_vars (fp, changes_only); + } + + public: + VAR_SUB_MENU ( //constructor + //variable to edit + const char *menu_text, MENU_ROOT * sub_menu + ):LEAF_MENU_NODE (menu_text) { + root = sub_menu; + sub_window = NULL; + } + + void child_closed() { + sub_window = NULL; + } +}; +#endif diff --git a/display/tessio.h b/display/tessio.h new file mode 100644 index 0000000000..a63e7a376a --- /dev/null +++ b/display/tessio.h @@ -0,0 +1,110 @@ +/********************************************************************** + * File: tessio.h (Formerly tessread.h) + * Description: Read/write Tesseract format row files. + * Author: Ray Smith + * Created: Wed Oct 09 15:02:46 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSIO_H +#define TESSIO_H + +#include +#include "tessclas.h" +#include "notdll.h" + +TEXTROW *get_tess_row_file( //open read & close + const char *name, //file name + TPOINT *topright //corner + ); +TBLOB *get_tess_blob_file( //open read & close + const char *name, //file name + TPOINT *topright //corner + ); +TEXTROW *readrows( //read row file + int gphfd, /*file to read */ + int count, /*number expected */ + TPOINT *imagesize //size of image + ); +TWERD *readwords( //read some words + int gphfd, /*file to read */ + int count, /*number expected */ + TEXTROW *row, /*row it comes from */ + TPOINT *imagesize /*size of image */ + ); +TBLOB *readblobs( //read some blobs + int gphfd, /*file to read */ + int count, /*number expected */ + TPOINT *imagesize /*size of image */ + ); +char *readratings( //get a string + int gphfd, /*file to read */ + int ratingspace /*size to read */ + ); +void readoutlines( //read some outlines + int gphfd, /*file to read */ + TESSLINE **outlines, /*array of ptrs */ + int outlinecount /*no to read */ + ); +int readgph( //read with testing + int fd, /*file to read */ + void *start, /*buffer to write */ + int size, /*amount to write */ + int checkeof /*give error on eof? */ + ); +void write_row( //write a row + FILE *name, //file to write + TEXTROW *row /*row to write */ + ); +void write_error_row( //write special row + FILE *name, /*file name */ + TEXTROW *row, /*row to write */ + int wordcount /*no of words to go */ + ); +void write_error_blob( //write special blob + FILE *name, /*file name */ + TBLOB *blob, /*blob to write */ + char *charlist, /*true chars */ + int charcount /*no of true chars */ + ); +void write_error_word( //write special word + FILE *name, /*file name */ + TWERD *word, /*word to write */ + char *charlist, /*true chars */ + int charcount /*no of true chars */ + ); +void writeblob( //write a blob + FILE *name, /*file to write */ + TBLOB *blob /*blob to write */ + ); +void serial_outlines( //serialize + FILE *name, /*file to write to */ + TBLOB *blob, /*current blob */ + register TESSLINE *outline, /*current outline */ + int *outlineno /*current serial no */ + ); +int countloop( //count loopsize + register BYTEVEC *vector /*vectors to count */ + ); +int outlineserial( //get serial no + register TESSLINE *outline, /*start of serach */ + register TESSLINE *target, /*outline to find */ + int serial /*serial no so far */ + ); +void writegph( //interface to fwrite + FILE *name, /*file to write */ + void *start, /*buffer to write */ + int size /*amount to write */ + ); +#endif diff --git a/display/varabled.cpp b/display/varabled.cpp new file mode 100644 index 0000000000..aea8973e21 --- /dev/null +++ b/display/varabled.cpp @@ -0,0 +1,283 @@ +/********************************************************************** + * File: varabled.cpp (Formerly varedit.c) + * Description: Variables Editor + * Author: Phil Cheatle + * Created: Mon Nov 11 09:51:58 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include "varable.h" +#include "varblmen.h" +#include "varblwin.h" +#include "varabled.h" +#include "cmndwin.h" +#include "mainblk.h" + +#define VARDIR "configs/" /*variables files */ + +const ERRCODE NO_VARIABLES_TO_EDIT = "No Variables defined to edit"; + +/********************************************************************** + * build_list_of_all_leaves() + * + * Generate a list of menu leaves for each of the variables. The list is + * heterogeneous, different menu leaf types for each variable type. + **********************************************************************/ + +MENU_L_LIST *build_list_of_all_leaves() { //find all variables + INT_VARIABLE_C_IT int_it (INT_VARIABLE::get_head ()); + BOOL_VARIABLE_C_IT bool_it (BOOL_VARIABLE::get_head ()); + STRING_VARIABLE_C_IT str_it (STRING_VARIABLE::get_head ()); + double_VARIABLE_C_IT dbl_it (double_VARIABLE::get_head ()); + + MENU_L_LIST *all_leaves_list = new MENU_L_LIST; + MENU_L_IT it; + + it.set_to_list (all_leaves_list); + + for (int_it.mark_cycle_pt (); !int_it.cycled_list (); int_it.forward ()) + it.add_to_end (new MENU_L (new INT_VAR_MENU_LEAF (int_it.data ()))); + + for (bool_it.mark_cycle_pt (); !bool_it.cycled_list (); bool_it.forward ()) + it.add_to_end (new MENU_L (new BOOL_VAR_MENU_LEAF (bool_it.data ()))); + + for (str_it.mark_cycle_pt (); !str_it.cycled_list (); str_it.forward ()) + it.add_to_end (new MENU_L (new STR_VAR_MENU_LEAF (str_it.data ()))); + + for (dbl_it.mark_cycle_pt (); !dbl_it.cycled_list (); dbl_it.forward ()) + it.add_to_end (new MENU_L (new DBL_VAR_MENU_LEAF (dbl_it.data ()))); + + return all_leaves_list; +} + + +/********************************************************************** + * build_main_var_menu() + * + * Extract each element from the sorted, non empty list of menu leaves. Add + * each to the appropriate menu. Note that ALL menus for ALL variable editor + * windows are built at this stage. The VAR_SUB_MENU menu leaves used in the + * leaves of the top level window hold the root menus for the sub windows for + * use if and when that window is created. A little wastful on space perhaps, + * but easier than reconstructing/sorting/etc the all_leaves_list. + * + **********************************************************************/ + +MENU_ROOT *build_main_var_menu( //build menus + MENU_L_LIST *all_leaves_list) { + MENU_L_IT it; + MENU_L_LIST sub_window_list; + MENU_L_LIST sub_menu_list; + + MENU_ROOT *main_root; + VAR_NON_RADIO_MENU *main_menu; + NON_RADIO_MENU *std_menu; + MENU_ROOT *main_misc_root = NULL; + VAR_NON_RADIO_MENU *main_misc_menu = NULL; + MENU_ROOT *sub_window_root; + VAR_NON_RADIO_MENU *current_sub_win_menu; + VAR_NON_RADIO_MENU *sub_window_misc_menu; + MENU_L *leaf; + const char *full_name; + char first_word[80]; + char first_two_words[80]; + int header_len; + STRING varfile; //save config filename + + main_root = new MENU_ROOT; + main_menu = new VAR_NON_RADIO_MENU ("SubMenus"); + main_root->add_child (main_menu); + + while (!all_leaves_list->empty ()) { + // Whatever is left + it.set_to_list (all_leaves_list); + full_name = (it.data ()->ptr)->cmp_str (); + // 1st word delim by _ + get_first_words (full_name, 1, first_word); + extract_sublist(all_leaves_list, first_word, &sub_window_list); + + //addto main misc menu + if (sub_window_list.singleton ()) { + if (main_misc_root == NULL) { + main_misc_root = new MENU_ROOT; + main_misc_menu = new VAR_NON_RADIO_MENU ("Miscellaneous"); + main_misc_root->add_child (main_misc_menu); + } + it.set_to_list (&sub_window_list); + leaf = it.extract (); + leaf->ptr->new_label (full_name); + main_misc_menu->add_child (leaf); + } + else { //build subwindow menu + sub_window_root = new MENU_ROOT; + main_menu->add_child (new VAR_SUB_MENU (first_word, + sub_window_root)); + sub_window_misc_menu = new VAR_NON_RADIO_MENU ("Miscellaneous"); + sub_window_misc_menu-> + add_child (new + SIMPLE_MENU_LEAF ("Kill Sub Window", KILL_WINDOW_CMD)); + while (!sub_window_list.empty ()) { + it.set_to_list (&sub_window_list); + full_name = it.data ()->ptr->cmp_str (); + get_first_words (full_name, 2, first_two_words); + extract_sublist(&sub_window_list, first_two_words, &sub_menu_list); + + it.set_to_list (&sub_menu_list); + //addto subwin misc + if (sub_menu_list.singleton ()) { + leaf = it.extract (); + leaf->ptr->new_label (full_name); + sub_window_misc_menu->add_child (leaf); + } + else { + //build sub menu + current_sub_win_menu = + new VAR_NON_RADIO_MENU(first_two_words); + sub_window_root->add_child (current_sub_win_menu); + header_len = strlen (first_two_words); + while (!sub_menu_list.empty ()) { + it.set_to_list (&sub_menu_list); + leaf = it.extract (); + leaf->ptr->new_label (leaf->ptr->cmp_str () + + header_len); + current_sub_win_menu->add_child (leaf); + } + } + } + sub_window_root->add_child (sub_window_misc_menu); + } + } + if (main_misc_root != NULL) + main_root->add_child (main_misc_root); + + varfile = datadir; + varfile += VARDIR; /*variables dir */ + varfile += "edited"; /*actual name */ + + std_menu = new NON_RADIO_MENU ("Build Config File"); + main_root->add_child (std_menu); + std_menu->add_child (new VARIABLE_MENU_LEAF ("All Variables", + WRITE_ALL_CMD, + varfile.string ())); + std_menu->add_child (new VARIABLE_MENU_LEAF ("Changed Variables Only", + WRITE_CHANGED_CMD, + varfile.string ())); + return main_root; +} + + +/********************************************************************** + * extract_sublist() + * + * Extract a sublist containing elements whose name initially matches the + * specified string + * + **********************************************************************/ + +void extract_sublist( //remove initial items + MENU_L_LIST *source_list, //source list + char *leading_str, //string to match + MENU_L_LIST *extracted_list //extracted list + ) { + MENU_L_IT start_it(source_list); + MENU_L_IT end_it(source_list); + int match_len = strlen (leading_str); + + while (!end_it.at_last () && + (strncmp (leading_str, end_it.data_relative (1)->ptr->cmp_str (), + match_len) == 0)) + end_it.forward (); + extracted_list->assign_to_sublist (&start_it, &end_it); +} + + +/********************************************************************** + * get_first_words() + * + * Copy the first N words from the source string to the target string. + * Words are delimited by "_" + * + **********************************************************************/ + +void get_first_words( //copy first N words + const char *s, //source string + int n, //number of words + char *t //target string + ) { + int full_length = strlen (s); + int reqd_len = 0; //No. of chars requird + const char *next_word = s; + + while ((n > 0) && reqd_len < full_length) { + reqd_len += strcspn (next_word, "_") + 1; + next_word += reqd_len; + n--; + } + strncpy(t, s, reqd_len); + t[reqd_len] = '\0'; //ensure null terminal +} + + +/********************************************************************** + * menu_item_sorter() LIST SORT COMPARATOR + * + * Given two MENU_L items, sort them on the basis of their cmp_str s + * + **********************************************************************/ + +int menu_item_sorter( //sorter + const void *item1, + const void *item2) { + MENU_L *menu_l_1 = (*(MENU_L **) item1); + MENU_L *menu_l_2 = (*(MENU_L **) item2); + + MENU_NODE *node1; + MENU_NODE *node2; + + const char *str1; + const char *str2; + + node1 = menu_l_1->ptr; + node2 = menu_l_2->ptr; + + str1 = node1->cmp_str (); + str2 = node2->cmp_str (); + + return strcmp (str1, str2); +} + + +/********************************************************************** + * start_variables_editor() + * + * Create the top level variables editor window. Build all the menus for all + * the subwindows in the process. + **********************************************************************/ + +void start_variables_editor() { //create top level win + MENU_L_LIST *all_leaves_list = new MENU_L_LIST; + MENU_L_IT it; + + all_leaves_list = build_list_of_all_leaves (); + it.set_to_list (all_leaves_list); + if (it.empty ()) { + NO_VARIABLES_TO_EDIT.error ("start_variables_editor", LOG, NULL); + } + it.sort (&menu_item_sorter); + + new VARIABLES_WINDOW ("VarEditorMAIN", + build_main_var_menu (all_leaves_list), NULL); +} diff --git a/display/varabled.h b/display/varabled.h new file mode 100644 index 0000000000..fa8b6b6555 --- /dev/null +++ b/display/varabled.h @@ -0,0 +1,41 @@ +/********************************************************************** + * File: varabled.h (Formerly varedit.h) + * Description: Variables Editor + * Author: Phil Cheatle + * Created: Mon Nov 11 09:51:58 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef VARABLED_H +#define VARABLED_H + //find all variables +MENU_L_LIST *build_list_of_all_leaves(); +MENU_ROOT *build_main_var_menu( //build menus + MENU_L_LIST *all_leaves_list); + +void extract_sublist( //remove initial items + MENU_L_LIST *source_list, //source list + char *leading_str, //string to match + MENU_L_LIST *extracted_list //extracted list + ); +void get_first_words( //copy first N words + const char *s, //source string + int n, //number of words + char *t //target string + ); +int menu_item_sorter( //sorter + const void *item1, + const void *item2); +void start_variables_editor(); //create top level win +#endif diff --git a/display/varblmen.cpp b/display/varblmen.cpp new file mode 100644 index 0000000000..7cab2d4ac0 --- /dev/null +++ b/display/varblmen.cpp @@ -0,0 +1,329 @@ +/********************************************************************** + * File: varblmen.cpp (Formerly varmenu.c) + * Description: Variables menu subclasses + * Author: Phil Cheatle + * Created: Wed Nov 13 11:17:26 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "varblmen.h" +#include "varblwin.h" + +/********************************************************************** + *********************************************************************** + * + * BOOL_VAR_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * BOOL_VAR_MENU_LEAF::event( ) + * + * Event selection - Toggle value + **********************************************************************/ + +void BOOL_VAR_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *, //Command selected + char * //Edited value + ) { + char msg[MAX_CHARS + 1]; + + if (box.contains (pt)) { + var->set_value (!(BOOL8) * var); + sprintf (msg, "TOGGLED: %s", var->info_str ()); + cmd_win->msg (msg); + changed = TRUE; + } +} + + +/********************************************************************** + * BOOL_VAR_MENU_LEAF::plotx( ) + * + * The real plot. Positions assumed already calculated. + **********************************************************************/ + +void BOOL_VAR_MENU_LEAF::plotx( //draw it + WINDOW window //in this window + ) { + if ((BOOL8) * var) { + text_color_index(window, BLACK); + fill_color_index(window, WHITE); + } + + LEAF_MENU_NODE::plotx(window); + + text_color_index(window, WHITE); + fill_color_index(window, GREY); +} + + +/********************************************************************** + * BOOL_VAR_MENU_LEAF::write_vars( ) + * + **********************************************************************/ + +void BOOL_VAR_MENU_LEAF::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + if (!changes_only || changed) { + fprintf (fp, "%-25s %-12s # %s\n", + var->name_str (), + (BOOL8) * var ? "TRUE" : "FALSE", var->info_str ()); + } +} + + +/********************************************************************** + *********************************************************************** + * + * DBL_VAR_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * DBL_VAR_MENU_LEAF::event( ) + * + * Event selection - Toggle value + **********************************************************************/ + +void DBL_VAR_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *, //Command selected + char * //Edited value + ) { + char ascii_new_value[MAX_CHARS + 1]; + const char *prompt_msg; + double new_value; + + if (box.contains (pt)) { + sprintf (ascii_new_value, "%g", (double) *var); + prompt_msg = var->info_str (); + while (cmd_win->internal_prompt (prompt_msg, ascii_new_value)) { + if (sscanf (ascii_new_value, "%lf", &new_value) == 1) { + if (new_value != (double) *var) { + var->set_value (new_value); + cmd_win->msg ("Value changed"); + changed = TRUE; + return; + } + else { + cmd_win->msg ("Value unchanged"); + return; + } + } + else + prompt_msg = "Invalid value for double - try again or click"; + } + } +} + + +/********************************************************************** + * DBL_VAR_MENU_LEAF::write_vars( ) + * + **********************************************************************/ + +void DBL_VAR_MENU_LEAF::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + if (!changes_only || changed) { + fprintf (fp, "%-25s %-12e # %s\n", + var->name_str (), (double) *var, var->info_str ()); + } +} + + +/********************************************************************** + *********************************************************************** + * + * INT_VAR_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * INT_VAR_MENU_LEAF::event( ) + * + * Event selection - Toggle value + **********************************************************************/ + +void INT_VAR_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *, //Command selected + char * //Edited value + ) { + char ascii_new_value[MAX_CHARS + 1]; + const char *prompt_msg; + INT32 new_value; + + if (box.contains (pt)) { + sprintf (ascii_new_value, INT32FORMAT, (INT32) * var); + prompt_msg = var->info_str (); + while (cmd_win->internal_prompt (prompt_msg, ascii_new_value)) { + if (sscanf (ascii_new_value, INT32FORMAT, &new_value) == 1) { + if (new_value != (INT32) * var) { + var->set_value (new_value); + cmd_win->msg ("Value changed"); + changed = TRUE; + return; + } + else { + cmd_win->msg ("Value unchanged"); + return; + } + } + else + prompt_msg = "Invalid value for INT32 - try again or click"; + } + } +} + + +/********************************************************************** + * INT_VAR_MENU_LEAF::write_vars( ) + * + **********************************************************************/ + +void INT_VAR_MENU_LEAF::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + if (!changes_only || changed) { + fprintf (fp, "%-25s %-12d # %s\n", + var->name_str (), (int) *var, var->info_str ()); + } +} + + +/********************************************************************** + *********************************************************************** + * + * STR_VAR_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * STR_VAR_MENU_LEAF::event( ) + * + * Event selection - Toggle value + **********************************************************************/ + +void STR_VAR_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *, //Command selected + char * //Edited value + ) { + char ascii_new_value[MAX_CHARS + 1]; + + if (box.contains (pt)) { + sprintf (ascii_new_value, "%s", ((STRING) * var).string ()); + if (cmd_win->internal_prompt (var->info_str (), ascii_new_value)) { + if (strcmp (ascii_new_value, ((STRING) * var).string ()) != 0) { + var->set_value (ascii_new_value); + cmd_win->msg ("Value changed"); + changed = TRUE; + return; + } + else { + cmd_win->msg ("Value unchanged"); + return; + } + } + } +} + + +/********************************************************************** + * STR_VAR_MENU_LEAF::write_vars( ) + * + **********************************************************************/ + +void STR_VAR_MENU_LEAF::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + if (!changes_only || changed) { + fprintf (fp, "%-25s %-12s # %s\n", + var->name_str (), + ((STRING) * var).string (), var->info_str ()); + } +} + + +/********************************************************************** + *********************************************************************** + * + * VAR_NON_RADIO_MENU MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * VAR_NON_RADIO_MENU::write_vars( ) + * + * Just propogate down the menu tree + **********************************************************************/ + +void VAR_NON_RADIO_MENU::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + MENU_L_IT it(&menu_list); + + fprintf (fp, "\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->ptr->write_vars (fp, changes_only); +} + + +/********************************************************************** + *********************************************************************** + * + * VAR_SUB_MENU MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * VAR_SUB_MENU::event( ) + * + * Event selection - Toggle value + **********************************************************************/ + +void VAR_SUB_MENU::event( //User clicked... + COMMAND_WINDOW *win, //For UI, update etc + FCOORD pt, //here + INT32 *, //Command selected + char * //Edited value + ) { + if (box.contains (pt)) { + if (sub_window != NULL) + win->msg ("Sub menu is already open!"); + else { + sub_window = new VARIABLES_WINDOW (name.string (), root, this); + } + } +} diff --git a/display/varblmen.h b/display/varblmen.h new file mode 100644 index 0000000000..5901c0d52f --- /dev/null +++ b/display/varblmen.h @@ -0,0 +1,203 @@ +/********************************************************************** + * File: varblmen.h (Formerly varmenu.h) + * Description: Variables menu subclasses + * Author: Phil Cheatle + * Created: Wed Nov 13 11:17:26 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef VARBLMEN_H +#define VARBLMEN_H + +#include "sbdmenu.h" +#include "submen.h" +#include "notdll.h" + +/********************************************************************** + +Class BOOL_VAR_MENU_LEAF - Boolean Variable leaf + +**********************************************************************/ + +class BOOL_VAR_MENU_LEAF:public LEAF_MENU_NODE +{ + private: + BOOL_VARIABLE * var; //The variable + BOOL8 changed; //User modified? + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + void plotx( //draw it + WINDOW window); //in this window + + const char *cmp_str() { //get ptr to name + return var->name_str (); + } + + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? + + public: + BOOL_VAR_MENU_LEAF ( //constructor + BOOL_VARIABLE * this_var //variable to edit + ): LEAF_MENU_NODE ("") { + var = this_var; + changed = FALSE; + } +}; + +/********************************************************************** + +Class DBL_VAR_MENU_LEAF - Double Variable leaf + +**********************************************************************/ + +class DBL_VAR_MENU_LEAF:public LEAF_MENU_NODE +{ + private: + double_VARIABLE * var; //The variable + BOOL8 changed; //User modified? + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + const char *cmp_str() { //get ptr to name + return var->name_str (); + } + + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? + + public: + DBL_VAR_MENU_LEAF ( //constructor + double_VARIABLE * this_var //variable to edit + ): LEAF_MENU_NODE ("") { + var = this_var; + changed = FALSE; + } +}; + +/********************************************************************** + +Class INT_VAR_MENU_LEAF - Integer Variable leaf + +**********************************************************************/ + +class INT_VAR_MENU_LEAF:public LEAF_MENU_NODE +{ + private: + INT_VARIABLE * var; //The variable + BOOL8 changed; //User modified? + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + const char *cmp_str() { //get ptr to name + return var->name_str (); + } + + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? + + public: + INT_VAR_MENU_LEAF ( //constructor + INT_VARIABLE * this_var //variable to edit + ): LEAF_MENU_NODE ("") { + var = this_var; + changed = FALSE; + } +}; + +/********************************************************************** + +Class STR_VAR_MENU_LEAF - String Variable leaf + +**********************************************************************/ + +class STR_VAR_MENU_LEAF:public LEAF_MENU_NODE +{ + private: + STRING_VARIABLE * var; //The variable + BOOL8 changed; //User modified? + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + const char *cmp_str() { //get ptr to name + return var->name_str (); + } + + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? + + public: + STR_VAR_MENU_LEAF ( //constructor + STRING_VARIABLE * this_var //variable to edit + ): LEAF_MENU_NODE ("") { + var = this_var; + changed = FALSE; + } +}; + +/********************************************************************** + +Class VAR_NON_RADIO_MENU - A sub menu containing variables + +**********************************************************************/ + +class VAR_NON_RADIO_MENU:public NON_LEAF_MENU_NODE +{ + private: + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? + + public: + VAR_NON_RADIO_MENU ( //constructor + const char *menu_text):NON_LEAF_MENU_NODE (menu_text) { + } + + void add_child( //add to sub-menu end //item to add + SIMPLE_MENU_LEAF *new_child) { + link_child(new_child); + } + + void add_child( //add to sub-menu end + VAR_SUB_MENU *new_child) { //item to add + link_child(new_child); + } + + void add_child( //add to sub-menu end + MENU_L *new_child) { //item to add + link_child_link(new_child); + } +}; +#endif diff --git a/display/varblwin.cpp b/display/varblwin.cpp new file mode 100644 index 0000000000..f0cd1d3b17 --- /dev/null +++ b/display/varblwin.cpp @@ -0,0 +1,279 @@ +/********************************************************************** + * File: varblwin.cpp (Formerly varwin.c) + * Description: Variables window subclass of COMMAND_WINDOW + * Author: Phil Cheatle + * Created: Thu Nov 14 15:40:26 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include "sbdmenu.h" +#include "submen.h" +#include "varblwin.h" +#include "notdll.h" + +const ERRCODE NOT_ASSOCIATED = "Cant find associated VARIABLES_WINDOW"; + +ASSOCIATION_LIST +VARIABLES_WINDOW::win_assocs; + // for initialisation + +ELISTIZE (ASSOC) +/********************************************************************** + * + * ASSOCIATION_LIST class member functions + * + **********************************************************************/ +/********************************************************************** + * ASSOCIATION_LIST::add() + * + * Add a new association + **********************************************************************/ +void ASSOCIATION_LIST::add( //Window handle + WINDOW new_fd, + VARIABLES_WINDOW *new_var_win //Associated var windw + ) { + ASSOC_IT it(&associations); + + it.add_to_end (new ASSOC (new_fd, new_var_win)); +} + + +/********************************************************************** + * ASSOCIATION_LIST::remove() + * + * Delete an association + **********************************************************************/ + +void ASSOCIATION_LIST::remove( // delete assoc + WINDOW fd //Window handle + ) { + ASSOC_IT it(&associations); + + if (!it.empty ()) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + if (fd == it.data ()->fd) { + it.extract (); + return; + } + } + } + NOT_ASSOCIATED.error ("ASSOCIATION_LIST::remove", ABORT, NULL); +} + + +/********************************************************************** + * ASSOCIATION_LIST::lookup() + * + * Find an association + **********************************************************************/ + +VARIABLES_WINDOW *ASSOCIATION_LIST::lookup( //find assoc + WINDOW fd //Window handle + ) { + ASSOC_IT it(&associations); + + if (!it.empty ()) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + if (fd == it.data ()->fd) + return it.data ()->var_win; + } + } + NOT_ASSOCIATED.error ("ASSOCIATION_LIST::lookup", ABORT, NULL); + return NULL; +} + + +/********************************************************************** + * ASSOCIATION_LIST::plot_all() + * + * Re plot all windows ( to reflect changes to bool vars ) + **********************************************************************/ + +void ASSOCIATION_LIST::plot_all() { //plot all wins + ASSOC_IT it(&associations); + + if (!it.empty ()) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->var_win->plot (); + } + } +} + + +/********************************************************************** + * ASSOCIATION_LIST::turn_off_interrupts() + * + * Disable interrupts for all windows of this class + **********************************************************************/ + +void ASSOCIATION_LIST::turn_off_interrupts() { + ASSOC_IT it(&associations); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + //disable interrupts + set_click_handler (it.data ()->fd, NULL); +} + + +/********************************************************************** + * ASSOCIATION_LIST::turn_on_interrupts() + * + * Re-enable interrupts for all windows of this class + **********************************************************************/ + +void ASSOCIATION_LIST::turn_on_interrupts( //handler + EVENT_HANDLER interrupt_proc) { + ASSOC_IT it(&associations); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + set_click_handler (it.data ()->fd, interrupt_proc); +} + + +/********************************************************************** + * + * VARIABLES_WINDOW class member functions + * + **********************************************************************/ + +/********************************************************************** + * VARIABLES_WINDOW::event() + * + * Do non standard event handling for specified commands + **********************************************************************/ + +void VARIABLES_WINDOW::v_event( //Process event + GRAPHICS_EVENT &g_event) { + INT32 cmd_event; //Command event type + char new_value[80]; //of menu item + char buff[80]; + + COMMAND_WINDOW::event(g_event, &cmd_event, new_value); + + switch (cmd_event) { + case NULL_COMMAND: + break; + case WRITE_ALL_CMD: + write_vars(new_value, FALSE); + break; + case WRITE_CHANGED_CMD: + write_vars(new_value, TRUE); + break; + case KILL_WINDOW_CMD: + win_assocs.remove (g_event.fd); + destroy_window (g_event.fd); + my_creator->child_closed (); + delete this; + break; + default: + sprintf (buff, "UNPROCESSED EVENT code " INT32FORMAT, cmd_event); + msg(buff); + } +} + + +/********************************************************************** + * VARIABLES_WINDOW::internal_prompt() + * + * Disable interrupts during prompting + * + **********************************************************************/ + +BOOL8 VARIABLES_WINDOW::internal_prompt( //Prompt user + const char *msg_str, //Prompt message + char *response_str //Response & Default + ) { + BOOL8 result; + + win_assocs.turn_off_interrupts (); + result = COMMAND_WINDOW::internal_prompt (msg_str, response_str); + win_assocs.turn_on_interrupts (VARIABLES_WINDOW::interrupt_handler); + set_click_handler(fd, VARIABLES_WINDOW::interrupt_handler); + return result; +} + + +/********************************************************************** + * VARIABLES_WINDOW::interrupt_handler() + * + * Forward the event to the VARIABLES_WINDOW responsible for handling events + * for the window where the event occured. + **********************************************************************/ + +void VARIABLES_WINDOW::interrupt_handler( //for any windw + GRAPHICS_EVENT *g_event //event recieved + ) { + (win_assocs.lookup (g_event->fd))->v_event (*g_event); + //forward to + overlap_picture_ops(TRUE); +} + + +/********************************************************************** + * VARIABLES_WINDOW::VARIABLES_WINDOW() + * + * Constructor for a variables window + **********************************************************************/ + +VARIABLES_WINDOW::VARIABLES_WINDOW ( + //constructor +const char *name, //window name +MENU_ROOT * menu_root, //menu +VAR_SUB_MENU * creator //who created me? +): +COMMAND_WINDOW(name, menu_root) { + win_assocs.add (fd, this); + my_creator = creator; + set_click_handler(fd, VARIABLES_WINDOW::interrupt_handler); +} + + +/********************************************************************** + * VARIABLES_WINDOW::write_vars() + * + * Write the variables to a config file + **********************************************************************/ + +void VARIABLES_WINDOW::write_vars( //Build config file + char *filename, // in this file + BOOL8 changes_only // Changed vars only? + ) { + FILE *fp; //input file + char msg_str[MAX_CHARS + 1]; + char response_str[MAX_CHARS + 1]; + char *token; //first response token + + //if file exists + if ((fp = fopen (filename, "r")) != NULL) { + fclose(fp); + sprintf (msg_str, "Overwrite file " "%s" "? (Y/N)", filename); + response_str[0] = '\0'; + if (!prompt (msg_str, response_str)) + return; + token = strtok (response_str, " "); + if (tolower (token[0]) != 'y') + return; // dont write + } + + fp = fopen (filename, "w"); //can we write to it? + if (fp == NULL) { + sprintf (msg_str, "Cant write to file " "%s" "", filename); + msg(msg_str); + return; + } + menu_root->write_vars (fp, changes_only); + fclose(fp); +} diff --git a/display/varblwin.h b/display/varblwin.h new file mode 100644 index 0000000000..900473a0fc --- /dev/null +++ b/display/varblwin.h @@ -0,0 +1,131 @@ +/********************************************************************** + * File: varblwin.h (Formerly varwin.h) + * Description: Variables window subclass of COMMAND_WINDOW + * Author: Phil Cheatle + * Created: Thu Nov 14 15:40:26 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef VARBLWIN_H +#define VARBLWIN_H + +#include "sbdmenu.h" +#include "notdll.h" +#include "notdll.h" + +#define KILL_WINDOW_CMD 1 +#define WRITE_ALL_CMD 2 +#define WRITE_CHANGED_CMD 3 + +class VARIABLES_WINDOW; //Fwd Decl + +/********************************************************************** + * + * ASSOC class + * + * Maintain an association between a window handle and the VARIABLES_WINDOW + * which manages events in that window. + **********************************************************************/ + +class ASSOC:public ELIST_LINK +{ + friend class ASSOCIATION_LIST; + + WINDOW fd; //Window handle + VARIABLES_WINDOW *var_win; //Associated var windw + + public: + ASSOC() { + } //contstructor + + ASSOC( //contstructor + WINDOW new_fd, //Window handle //Associated var windw + VARIABLES_WINDOW *new_var_win) { + fd = new_fd; + var_win = new_var_win; + } +}; + +ELISTIZEH (ASSOC) +/********************************************************************** + * + * ASSOCIATION_LIST class + * + * Maintain a list of associations between window handles and the + * VARIABLES_WINDOWs which manages events in those windows. + **********************************************************************/ +class ASSOCIATION_LIST +{ + ASSOC_LIST associations; //List of pairs + + public: + ASSOCIATION_LIST() { + }; + + void add(WINDOW new_fd, //Window handle //Associated var windw + VARIABLES_WINDOW *new_var_win); + + //Window handle + VARIABLES_WINDOW *lookup(WINDOW fd); + + void plot_all(); //Redisplay all wins + + void remove(WINDOW fd); //Window handle + + void turn_off_interrupts(); //for all windws listd + + void turn_on_interrupts( //for all windws listd //handler + EVENT_HANDLER interrupt_proc); +}; + +/********************************************************************** + * + * VARIABLES_WINDOW class + * + * A subclass of the basic COMMAND_WINDOW which is used for variables editor + * windows. The chief difference is that this window type awaits interrupts. + **********************************************************************/ + +class VARIABLES_WINDOW:public COMMAND_WINDOW +{ + private: + //fd -> var win assocs + static ASSOCIATION_LIST win_assocs; + VAR_SUB_MENU *my_creator; //tell it when I die + + void write_vars( //Build config file + char *filename, // in this file + BOOL8 changes_only); // Changed vars only? + + //for all windows + static void interrupt_handler(GRAPHICS_EVENT *g_event); + + void v_event( //for specified window + GRAPHICS_EVENT &g_event); + + public: + VARIABLES_WINDOW( //constructor + const char *name, //window name + MENU_ROOT *menu_root, + VAR_SUB_MENU *creator); + + BOOL8 internal_prompt( //Prompt user(No plot) + const char *prompt_str, //Prompt message + char *response); //Response & Default + + static void plot_all() { //Redisplay all vars + win_assocs.plot_all (); + } +}; +#endif diff --git a/doc/main.txt b/doc/main.txt new file mode 100644 index 0000000000..16a5598bd6 --- /dev/null +++ b/doc/main.txt @@ -0,0 +1,10 @@ +/*! + +\mainpage Overview of Tesseract OCR + +\section SecIntroduction Introduction + +TO BE WRITTEN + +*/ + diff --git a/image/Makefile.am b/image/Makefile.am new file mode 100644 index 0000000000..d22c42b481 --- /dev/null +++ b/image/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + bitstrm.h imgbmp.h imgerrs.h img.h imgio.h imgs.h \ + imgtiff.h imgunpk.h + +noinst_LIBRARIES = libtesseract_image.a +libtesseract_image_a_SOURCES = \ + imgbmp.cpp imgio.cpp imgs.cpp imgtiff.cpp bitstrm.cpp diff --git a/image/Makefile.in b/image/Makefile.in new file mode 100644 index 0000000000..82fc322933 --- /dev/null +++ b/image/Makefile.in @@ -0,0 +1,531 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = image +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_image_a_AR = $(AR) $(ARFLAGS) +libtesseract_image_a_LIBADD = +am_libtesseract_image_a_OBJECTS = imgbmp.$(OBJEXT) imgio.$(OBJEXT) \ + imgs.$(OBJEXT) imgtiff.$(OBJEXT) bitstrm.$(OBJEXT) +libtesseract_image_a_OBJECTS = $(am_libtesseract_image_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_image_a_SOURCES) +DIST_SOURCES = $(libtesseract_image_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/ccutil +EXTRA_DIST = \ + bitstrm.h imgbmp.h imgerrs.h img.h imgio.h imgs.h \ + imgtiff.h imgunpk.h + +noinst_LIBRARIES = libtesseract_image.a +libtesseract_image_a_SOURCES = \ + imgbmp.cpp imgio.cpp imgs.cpp imgtiff.cpp bitstrm.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu image/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu image/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_image.a: $(libtesseract_image_a_OBJECTS) $(libtesseract_image_a_DEPENDENCIES) + -rm -f libtesseract_image.a + $(libtesseract_image_a_AR) libtesseract_image.a $(libtesseract_image_a_OBJECTS) $(libtesseract_image_a_LIBADD) + $(RANLIB) libtesseract_image.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitstrm.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imgbmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imgio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imgs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imgtiff.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/image/bitstrm.cpp b/image/bitstrm.cpp new file mode 100644 index 0000000000..28f68f665d --- /dev/null +++ b/image/bitstrm.cpp @@ -0,0 +1,157 @@ +/********************************************************************** + * File: bitstrm.c (Formerly bits.c) + * Description: Bitstream read/write class member functions. + * Author: Ray Smith + * Created: Tue Feb 19 10:59:44 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#ifdef __MSW32__ +#include +#else +#include +#endif +#include "fileerr.h" +#include "bitstrm.h" + +const UINT16 +R_BITSTREAM::bitmasks[17] = { + 0, 1, 3, 7, 15, 31, 63, 127, 255, + 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535 +}; + +/********************************************************************** + * R_BITSTREAM::open + * + * Establish a bitstream for reading. + **********************************************************************/ + +UINT16 R_BITSTREAM::open( //open for read + int fd //file to read + ) { + bitfd = fd; + bufsize = read (fd, (char *) bitbuf, BITBUFSIZE * sizeof (UINT8)); + //fill buffer + if (bufsize < 0) { + READFAILED.error ("R_BITSTREAM::open", LOG, NULL); + return 0; + } + bitword = bitbuf[0] | (bitbuf[1] << 8); + bitindex = 2; + bitbit = 16; + return (UINT16) bitword; +} + + +/********************************************************************** + * R_BITSTREAM::read_code + * + * Remove a code from the bitstream. + **********************************************************************/ + +UINT16 R_BITSTREAM::read_code( //take code out + UINT8 length //length of code + ) { + bitbit -= length; //no of bits left + bitword >>= length; //remove bits + while (bitbit < 16) { + //get next byte + bitword |= bitbuf[bitindex++] << bitbit; + bitbit += 8; + if (bitindex >= bufsize) { + bufsize = + read (bitfd, (char *) bitbuf, BITBUFSIZE * sizeof (UINT8)); + if (bufsize < 0) { + READFAILED.error ("R_BITSTREAM::read_code", LOG, NULL); + return 0; + } + bitindex = 0; //newly filled buffer + } + } + return (UINT16) bitword; +} + + +/********************************************************************** + * R_BITSTREAM::masks + * + * Read a code from the static member. + **********************************************************************/ + +UINT16 R_BITSTREAM::masks( //take code out + INT32 index //length of code + ) { + return bitmasks[index]; +} + + +/********************************************************************** + * W_BITSTREAM::open + * + * Establish a bitstream for writing. + **********************************************************************/ + +void W_BITSTREAM::open( //open for write + int fd //file to write + ) { + bitfd = fd; + bitindex = 0; + bitword = 0; + bitbit = 0; +} + + +/********************************************************************** + * W_BITSTREAM::write_code + * + * Add a code to the bitstream. + **********************************************************************/ + +INT8 W_BITSTREAM::write_code( //take code out + UINT16 code, //code to add + UINT8 length //length of code + ) { + if (length == 0) { + //flushing + if (bitbit > 0) + //get last byte + bitbuf[bitindex++] = (UINT8) bitword; + if ((bitindex > 0) && + (write (bitfd, (char *) bitbuf, bitindex * sizeof (UINT8)) != + (INT32) (bitindex * sizeof (UINT8)))) { + WRITEFAILED.error ("W_BITSTREAM::write_code", LOG, "Flushing"); + return -1; + } + } + else { + bitword |= code << bitbit; //add new code + bitbit += length; + while (bitbit >= 8) { + //get next byte + bitbuf[bitindex++] = (UINT8) bitword; + bitbit -= 8; + bitword >>= 8; + if (bitindex >= BITBUFSIZE) { + if (write (bitfd, (char *) bitbuf, bitindex * sizeof (UINT8)) + != (INT32) (bitindex * sizeof (UINT8))) { + WRITEFAILED.error ("W_BITSTREAM::write_code", LOG, NULL); + return -1; + } + bitindex = 0; //newly filled buffer + } + } + } + return 0; //success +} diff --git a/image/bitstrm.h b/image/bitstrm.h new file mode 100644 index 0000000000..c93383e382 --- /dev/null +++ b/image/bitstrm.h @@ -0,0 +1,73 @@ +/********************************************************************** + * File: bitstrm.h (Formerly bits.h) + * Description: R_BITSTREAM and W_BITSTREAM class definitions. + * Author: Ray Smith + * Created: Tue Feb 19 10:44:22 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BITSTRM_H +#define BITSTRM_H + +#include "host.h" + +#define BITBUFSIZE 8192 //bitstream buffer + +class DLLSYM R_BITSTREAM +{ + private: + int bitfd; //file descriptor + INT32 bitindex; //current byte + UINT32 bitword; //current word + INT32 bitbit; //current bit + INT32 bufsize; //size of buffer + UINT8 bitbuf[BITBUFSIZE]; //bitstream buffer + //for reading codes + static const UINT16 bitmasks[17]; + + public: + + R_BITSTREAM() { + }; //Null constructor + + UINT16 open( //open to read + int fd); //file to read + + UINT16 read_code( //read a code + UINT8 length); //bits to lose + UINT16 masks( //read a code + INT32 index); //bits to lose +}; + +class DLLSYM W_BITSTREAM +{ + private: + int bitfd; //file descriptor + INT32 bitindex; //current byte + UINT32 bitword; //current word + INT32 bitbit; //current bit + UINT8 bitbuf[BITBUFSIZE]; //bitstream buffer + + public: + W_BITSTREAM() { + }; //Null constructor + + void open( //open to write + int fd); //file to write + + INT8 write_code( //write a code + UINT16 code, //code to write + UINT8 length); //bits to lose +}; +#endif diff --git a/image/img.h b/image/img.h new file mode 100644 index 0000000000..7e3e2cf08c --- /dev/null +++ b/image/img.h @@ -0,0 +1,329 @@ +/********************************************************************** + * File: img.h (Formerly image.h) + * Description: Class definition for the IMAGE class. + * Author: Ray Smith + * Created: Thu Jun 07 13:42:37 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef IMG_H +#define IMG_H + +#include "memry.h" + +#define MAXIMAGEWIDTH (900*14) /*14inch * 400dpi */ + /*14inch * 400dpi */ +#define MAXIMAGEHEIGHT (900*14) + +#define COMPUTE_IMAGE_XDIM(xsize,bpp) ((bpp)>8 ? ((xsize)*(bpp)+7)/8 :((xsize)+8/(bpp)-1)/(8/(bpp))) + +typedef INT8 (*IMAGE_OPENER) (int, INT32 *, INT32 *, INT8 *, INT8 *, INT32 *); +typedef INT8 (*IMAGE_READER) (int, UINT8 *, INT32, INT32, INT8, INT32); +typedef INT8 (*IMAGE_WRITER) (int, UINT8 *, INT32, INT32, INT8, INT8, INT32); + +typedef UINT8 *COLOUR_PIX; //array of colours +enum COLOUR_PIX_NAME +{ + RED_PIX, + GREEN_PIX, + BLUE_PIX +}; + +class DLLSYM IMAGELINE; + +class DLLSYM IMAGE //encapsulated image +{ + public: + IMAGE(); //constructor + + ~IMAGE () { //destructor + destroy(); //free memory + } + + IMAGE & operator= ( //assignment + IMAGE & source); + + INT8 read_header( //get file header + const char *name); //name of image + + INT8 read( //get rest of image + INT32 buflines); //size of buffer + + INT8 write( //write image + const char *name); //name to write + + INT8 create( //create blank image + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel); //bpp required + + INT8 capture( //capture raw image + UINT8 *pixels, //pixels to capture + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel); //bpp required + + void destroy(); //destroy image + + INT32 get_xsize() { + return xsize; + } + //access function + INT32 get_ysize() { + return ysize; + } + //access function + INT8 get_bpp() { + return bpp; + } //access function + INT8 get_bps() { + return bps; + } //bits per sample + BOOL8 white_high() { //photo interp + return photo_interp; + } + UINT8 get_white_level() { //access function + return (1 << bpp) - 1; + } + INT32 get_res() { + return res; + } //access function + void set_res( //set resolution + INT32 resolution) { + res = resolution; + } + UINT8 *get_buffer() { + return image; + } + //access function + + UINT8 pixel( //access pixel + INT32 x, //x coord + INT32 y); //y coord + + void fast_get_line( //get image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf); //line to copy to + + void get_line( //get image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins); //size of margins + void get_column( //get image column + INT32 x, //coord to start at + INT32 y, //line to get + INT32 height, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins); //size of margins + + void fast_put_line( //put image line + INT32 x, //coord to start at + INT32 y, //line to put + INT32 width, //no of pixels to put + IMAGELINE *linebuf); //line to copy from + + void put_line( //put image line + INT32 x, //coord to start at + INT32 y, //line to put + INT32 width, //no of pixels to put + IMAGELINE *linebuf, //line to copy from + INT32 margins); //size of margins + void put_column( //put image column + INT32 x, //coord to start at + INT32 y, //line to put + INT32 height, //no of pixels to put + IMAGELINE *linebuf, //line to copy to + INT32 margins); //size of margins + + void check_legal_access( //check coords + INT32 x, //xcoord to check + INT32 y, + INT32 xext); //ycoord to check + + void convolver ( //Map fn over window + INT32 win_width, //Window width + INT32 win_height, //Window height + void (*convolve) ( //Conv Function + UINT8 ** pixels, //Of window + UINT8 bytespp, //1 or 3 for colour + INT32 win_wd, //Window width + INT32 win_ht, //Window height + UINT8 ret_white_value, //White value to RETURN + UINT8 * result)); //Result pixel(s) + + //copy rectangle + friend DLLSYM void copy_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords //shift to match bpp + INT32 ydest, + BOOL8 adjust_grey); + + //enlarge rectangle + friend DLLSYM void enlarge_sub_image(IMAGE *source, //source image + INT32 xstart, //scaled coords + INT32 ystart, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 xext, //extent to copy + INT32 yext, + INT32 scale, //scale factor + BOOL8 adjust_grey); //shift to match bpp + + //reduce rectangle + friend DLLSYM void fast_reduce_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //scale factor + BOOL8 adjust_grey); //shift to match bpp + + //reduce rectangle + friend DLLSYM void reduce_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //scale factor + BOOL8 adjust_grey); //shift to match bpp + + private: + INT8 bpp; //bits per pixel + INT8 bps; //bits per sample + INT8 bytespp; //per pixel + INT8 lineskip; //waste bytes on line + BOOL8 captured; //true if buffer captured + INT8 photo_interp; //interpretation + INT32 xsize, ysize; //size of image + INT32 res; //resolution + UINT8 *image; //the actual image + INT32 xdim; //bytes per line + INT32 bufheight; //height of buffer + int fd; //open file descriptor + IMAGE_READER reader; //reading function + INT32 ymin; //bottom line in mem + INT32 ymax; //top line in mem+1 + INT8 bufread( //read some more + INT32 y); //ycoord required +}; + +class DLLSYM IMAGELINE //one line of image +{ + public: + UINT8 * pixels; //image pixels + INT8 bpp; //bits per pixel + COLOUR_PIX operator[] ( //colour pixels + INT32 index) { + return &pixels[index * 3]; //coercion access op + } + + IMAGELINE() { //default constructor + linewidth = 0; + line = NULL; + pixels = line; + bpp = 8; + } + void init( //setup size + INT32 width) { //size of line + if (width <= 0) + width = MAXIMAGEWIDTH; + if (width > linewidth) { + if (line != NULL) + free_mem(line); + linewidth = width; + line = (UINT8 *) alloc_mem (linewidth * sizeof (UINT8)); + } + pixels = line; + bpp = 8; + } + ~IMAGELINE () { //destructor + if (line != NULL) + free_mem(line); + } + + void set_bpp( //For colour + INT8 new_bpp) { + if (new_bpp <= 8) + bpp = 8; + else + bpp = 24; + } + + void init() { + if (line == NULL) + init (0); + else { + pixels = line; + bpp = 8; + } + } + + friend void IMAGE::get_line( //copies a line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins); //size of margins + //copies a column + friend void IMAGE::get_column(INT32 x, //coord to start at + INT32 y, //line to get + INT32 height, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins); //size of margins + + friend void IMAGE::put_line( //writes a line + INT32 x, //coord to start at + INT32 y, //line to put + INT32 width, //no of pixels to put + IMAGELINE *linebuf, //line to copy from + INT32 margins); //size of margins + //writes a column + friend void IMAGE::put_column(INT32 x, //coord to start at + INT32 y, //line to put + INT32 height, //no of pixels to put + IMAGELINE *linebuf, //line to copy from + INT32 margins); //size of margins + + //may just change pointer + friend void IMAGE::fast_get_line(INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf); //line to copy to + + //may just change pointer + friend void IMAGE::fast_put_line(INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf); //line to copy to + + private: + UINT8 * line; //local buffer + INT32 linewidth; //width of buffer +}; +#endif diff --git a/image/imgbmp.cpp b/image/imgbmp.cpp new file mode 100644 index 0000000000..30bc001bbb --- /dev/null +++ b/image/imgbmp.cpp @@ -0,0 +1,223 @@ +/********************************************************************** + * File: imgbmp.c (Formerly lz.c) + * Description: bmp image reader/writer. + * Author: Ray Smith + * Created: Tue Jan 06 15:31:25 GMT 1998 + * + * (C) Copyright 1998, Ray Smith. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#ifdef __MSW32__ +#include +#else +#include +#endif +#include +#include "img.h" +#include "imgbmp.h" + +typedef struct +{ // bmfh + char bfType1; //'B' + char bfType2; //'M' +} BMPHEADER0; +typedef struct +{ // bmfh + UINT32 bfSize; //filesize + UINT16 bfReserved1; //zero + UINT16 bfReserved2; //zero + UINT32 bfOffBits; //offset to bitmap +} BMPHEADER; + +typedef struct +{ // bmih + UINT32 biSize; //size of struct + INT32 biWidth; //image width + INT32 biHeight; //image height + UINT16 biPlanes; //1 + UINT16 biBitCount; //bpp + UINT32 biCompression; //0 for uncompressed + UINT32 biSizeImage; //image size + INT32 biXPelsPerMeter; //res in pp metre + INT32 biYPelsPerMeter; + UINT32 biClrUsed; //0 or actual size of colour table + UINT32 biClrImportant; //usually 0 +} BMPHEADER2; + +typedef struct +{ // rgbq + UINT8 rgbBlue; + UINT8 rgbGreen; + UINT8 rgbRed; + UINT8 rgbReserved; //0 +} WIN32_RGBQUAD; + +/********************************************************************** + * open_bmp_image + * + * Read the header of a bmp format image and prepare to read the rest. + **********************************************************************/ + +INT8 open_bmp_image( //read header + int fd, //file to read + INT32 *xsize, //size of image + INT32 *ysize, + INT8 *bpp, //bits per pixel + INT8 *photo, + INT32 *res //resolution + ) { + UINT32 nread; //current bits + BMPHEADER0 head0; //first part of header + BMPHEADER head1; //first part of header + BMPHEADER2 head2; //first part of header + + *photo = 1; + nread = read (fd, &head0, sizeof (head0)); + if (nread != sizeof (head0)) + return -1; + nread = read (fd, &head1, sizeof (head1)); + if (nread != sizeof (head1)) + return -1; + nread = read (fd, &head2, sizeof (head2)); + if (nread != sizeof (head2)) + return -1; + + if (head0.bfType1 != 'B') + return -1; + if (head0.bfType2 != 'M') + return -1; + lseek (fd, head1.bfOffBits, SEEK_SET); + *bpp = head2.biBitCount; + *xsize = head2.biWidth; + *ysize = head2.biHeight; + *res = 300; //make up resolution + return -2; //success +} + + +/********************************************************************** + * read_bmp_image + * + * Read a whole lz format image and close the file. + **********************************************************************/ + +INT8 read_bmp_image( //read header + int fd, //file to read + UINT8 *pixels, //pixels of image + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 //bytes per line + ) { + UINT32 bpl; //bytes per line + UINT32 wpl; //words per line + UINT32 nread; //current bits + INT32 index; //to cols + + bpl = (xsize * bpp + 7) / 8; //bytes per line + wpl = (bpl + 3) / 4; + wpl *= 4; + for (index = 0; index < ysize; index++) { + nread = read (fd, pixels + bpl * (ysize - 1 - index), bpl); + if (nread != bpl) + return -1; + if (wpl != bpl) + lseek (fd, wpl - bpl, SEEK_CUR); + } + return 0; +} + + +/********************************************************************** + * write_bmp_image + * + * Write a whole lz format image and close the file. + **********************************************************************/ + +INT8 write_bmp_image( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8, + INT32 res //resolution + ) { + UINT32 bpl; //bytes per line + UINT32 wpl; //words per line + UINT32 nread; //current bits + INT32 cols; //entries in table + INT32 index; //to cols + BMPHEADER0 head0; //first part of header + BMPHEADER head1; //first part of header + BMPHEADER2 head2; //first part of header + WIN32_RGBQUAD coltab[256]; //colour table + + if (bpp == 24) + cols = 0; + else + cols = 1 << bpp; //size of colour table + bpl = (xsize * bpp + 7) / 8; //bytes per line + wpl = (bpl + 3) / 4; + + head2.biSize = sizeof (head2); //size of struct + head2.biWidth = xsize; //image width + head2.biHeight = ysize; //image height + head2.biPlanes = 1; //1 + head2.biBitCount = bpp; //bpp + head2.biCompression = 0; //0 for uncompressed + //image size + head2.biSizeImage = wpl * 4 * ysize; + //res in pp metre + head2.biXPelsPerMeter = (UINT32) (res * 39.37); + head2.biYPelsPerMeter = (UINT32) (res * 39.37); + head2.biClrUsed = cols; //0 or actual size of colour table + head2.biClrImportant = 0; //usually 0 + + head0.bfType1 = 'B'; + head0.bfType2 = 'M'; + head1.bfReserved1 = 0; //zero + head1.bfReserved2 = 0; //zero + //offset to bitmap + head1.bfOffBits = sizeof (head0) + sizeof (head1) + sizeof (head2) + sizeof (WIN32_RGBQUAD) * cols; + //filesize + head1.bfSize = head1.bfOffBits + head2.biSizeImage; + + for (index = 0; index < cols; index++) { + coltab[index].rgbBlue = index * 255 / (cols - 1); + coltab[index].rgbGreen = coltab[index].rgbBlue; + coltab[index].rgbRed = coltab[index].rgbBlue; + coltab[index].rgbReserved = 0; + } + + nread = write (fd, &head0, sizeof (head0)); + if (nread != sizeof (head0)) + return -1; + nread = write (fd, &head1, sizeof (head1)); + if (nread != sizeof (head1)) + return -1; + nread = write (fd, &head2, sizeof (head2)); + if (nread != sizeof (head2)) + return -1; + nread = write (fd, coltab, cols * sizeof (WIN32_RGBQUAD)); + if (nread != cols * sizeof (WIN32_RGBQUAD)) + return -1; + for (index = 0; index < ysize; index++) { + nread = write (fd, pixels + bpl * (ysize - 1 - index), wpl * 4); + if (nread != wpl * 4) + return -1; + } + close(fd); //done it + return 0; +} diff --git a/image/imgbmp.h b/image/imgbmp.h new file mode 100644 index 0000000000..69f08a8f81 --- /dev/null +++ b/image/imgbmp.h @@ -0,0 +1,50 @@ +/********************************************************************** + * File: imgbmp.c (Formerly lz.c) + * Description: bmp image reader/writer. + * Author: Ray Smith + * Created: Tue Jan 06 20:15:52 GMT 1998 + * + * (C) Copyright 1998, Ray Smith. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef IMGBMP_H +#define IMGBMP_H + +#include "host.h" + +INT8 open_bmp_image( //read header + int fd, //file to read + INT32 *xsize, //size of image + INT32 *ysize, + INT8 *bpp, //bits per pixel + INT8 *photo, + INT32 *res //resolution + ); +INT8 read_bmp_image( //read header + int fd, //file to read + UINT8 *pixels, //pixels of image + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 //bytes per line + ); +INT8 write_bmp_image( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8, + INT32 //resolution + ); +#endif diff --git a/image/imgerrs.h b/image/imgerrs.h new file mode 100644 index 0000000000..00e9f6672f --- /dev/null +++ b/image/imgerrs.h @@ -0,0 +1,35 @@ +/********************************************************************** + * File: imgerrs.h (Formerly imgerr.h) + * Description: Definitions of errors related to IMAGE operations. + * Author: Ray Smith + * Created: Tue Aug 14 10:10:53 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef IMGERRS_H +#define IMGERRS_H + +#include "errcode.h" + +const ERRCODE BADIMAGETYPE = "Unrecognized image type"; +const ERRCODE CANTREADIMAGETYPE = "Can't read this image type"; +const ERRCODE CANTWRITEIMAGETYPE = "Can't write this image type"; +const ERRCODE IMAGEUNDEFINED = "Attempt to operate on undefined image"; +const ERRCODE BADIMAGECOORDS = "Coordinates in image out of bounds"; +const ERRCODE BADIMAGESEEK = "Can't seek backwards in a buffered image!"; +const ERRCODE BADIMAGESIZE = "Illegal image size"; +const ERRCODE BADIMAGEFORMAT = "Illegal image format"; +const ERRCODE BADBPP = "Only 1,2,4,5,6,8 bpp are supported"; +const ERRCODE BADWINDOW = "Convolution window must have odd dimensions"; +#endif diff --git a/image/imgio.cpp b/image/imgio.cpp new file mode 100644 index 0000000000..49054cd47b --- /dev/null +++ b/image/imgio.cpp @@ -0,0 +1,321 @@ +/********************************************************************** + * File: imgio.c (Formerly imageio.c) + * Description: Controls image input/output and selection of format. + * Author: Ray Smith + * Created: Mon Jun 11 11:47:26 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#ifdef __MSW32__ +#include +#else +#include +#endif +#include +#include +#include +#include +#include "scanutils.h" +#include "stderr.h" +#include "fileerr.h" +#include "imgerrs.h" +#include "memry.h" +#include "imgs.h" +#include "imgbmp.h" +#include "imgtiff.h" +#include "imgio.h" + +#define DEFAULTIMAGETYPE "tif" //default to im files + +typedef struct +{ + const char *string; //extension + IMAGE_OPENER opener; //opening function + IMAGE_READER reader; //reading function + IMAGE_WRITER writer; //writing function +} IMAGETYPE; //image type record + +static IMAGETYPE imagetypes[] = { { + "TIF", + open_tif_image, + read_tif_image, + write_moto_tif + }, + { + "itf", + open_tif_image, + read_tif_image, + write_inverse_tif + }, + { + "tif", + open_tif_image, + read_tif_image, + write_intel_tif + }, + { + "bmp", + open_bmp_image, + read_bmp_image, + write_bmp_image + }, +}; //image readers/writers + +#define MAXIMAGETYPES (sizeof(imagetypes)/sizeof(IMAGETYPE)) + +/********************************************************************** + * name_to_image_type + * + * Convert a file name to an image type, picking defaults if it is + * has no extension, and complaining if the extension is not supported. + **********************************************************************/ + +static INT8 name_to_image_type( //get image type + const char *name //name of image + ) { + const char *nametype; //type part of name + INT8 type; //imagetypes index + + nametype = strrchr (name, '.');//find extension + if (nametype != NULL) + nametype++; //ptr to extension + else + nametype = DEFAULTIMAGETYPE; //had none + + //find type of image + for (type = 0; type < MAXIMAGETYPES && strcmp (imagetypes[type].string, nametype); type++); + if (type >= MAXIMAGETYPES) { + //unrecognized type + BADIMAGETYPE.error ("name_to_image_type", LOG, name); + return -1; + } + return type; +} + + +/********************************************************************** + * read_header + * + * Read the header of an image, typed according to the extension of + * the name. Return is 0 for success, -1 for failure. + **********************************************************************/ + +INT8 IMAGE::read_header( //get file header + const char *name //name of image + ) { + INT8 type; //image type + + destroy(); //destroy old image + //get type + type = name_to_image_type (name); + if (type < 0 || imagetypes[type].opener == NULL) { + CANTREADIMAGETYPE.error ("IMAGE::read_header", LOG, name); + return -1; //read not supported + } + #ifdef __UNIX__ + if ((fd = open (name, O_RDONLY)) < 0) + #endif + #if defined (__MSW32__) || defined (__MAC__) + if ((fd = open (name, O_RDONLY | O_BINARY)) < 0) + #endif + { + CANTOPENFILE.error ("IMAGE::read_header", LOG, name); + return -1; //failed + } + lineskip = + (*imagetypes[type].opener) (fd, &xsize, &ysize, &bpp, &photo_interp, + &res); + if (lineskip == -1) { + //get header + bpp = 0; //still empty + close(fd); + fd = -1; + return -1; //failed + } + if (res <= 0) + res = image_default_resolution; + // fprintf(stderr,"Image size=(%d,%d), bpp=%d\n", + // xsize,ysize,bpp); + //bytes per line + xdim = COMPUTE_IMAGE_XDIM (xsize, bpp); + bps = bpp == 24 ? 8 : bpp; + bytespp = (bpp + 7) / 8; + //funtion to read with + reader = imagetypes[type].reader; + return 0; //success +} + + +/********************************************************************** + * read + * + * Read a previously opened image file into memory. + * If buflines is 0, the whole image is read in one go. + * If buflines>0, memory space is reserved for reading just that many + * lines at once. + * As soon as a request is made to get a line past the end of the buffer, + * the buffer is re-read with a 50% overlap. + * Backward seeks are not allowed. + * Read returns -1 in case of failure or 0 if successful. + **********************************************************************/ + +INT8 IMAGE::read( //get rest of image + INT32 buflines //size of buffer + ) { + INT32 row; //image row + BOOL8 failed; //read failed + + if (fd < 0 || image != NULL) + IMAGEUNDEFINED.error ("IMAGE::read", ABORT, NULL); + + if (buflines <= 0 || buflines > ysize || reader == NULL) + buflines = ysize; //default to all + bufheight = buflines; + image = + (UINT8 *) alloc_big_mem ((size_t) (xdim * bufheight * sizeof (UINT8))); + if (image == NULL) { + MEMORY_OUT.error ("IMAGE::read", LOG, NULL); + destroy(); + return -1; + } + captured = FALSE; + ymax = ysize; + ymin = ysize - buflines; //amount of image read + if (reader != NULL && lineskip < 0) + failed = (*reader) (fd, image, xsize, ysize, bpp, xdim) < 0; + else { + if (lineskip == 0) + failed =::read (fd, (char *) image, + (size_t) (xdim * bufheight)) != xdim * bufheight; + else { + for (failed = FALSE, row = 0; row < bufheight && !failed; row++) { + failed =::read (fd, (char *) image + row * xdim, + (size_t) xdim) != xdim; + failed |= lseek (fd, lineskip, SEEK_CUR) < 0; + } + } + } + if (failed) { + READFAILED.error ("IMAGE::read", LOG, NULL); + destroy(); + return -1; //read failed + } + if (ymin <= 0) { + close(fd); //finished reading + fd = -1; //not open now + } + return 0; //success +} + + +/********************************************************************** + * bufread + * + * Read a bit more of an image into the buffer. + **********************************************************************/ + +INT8 IMAGE::bufread( //read more into buffer + INT32 y //required coord + ) { + INT32 readtop; //no of lines copied + INT32 linestoread; //no of lines to read + INT32 row; //row to read + BOOL8 failed; //read failed + + //copy needed? + if (y + bufheight / 2 >= ymin) { + //no of lines to move + readtop = y + bufheight / 2 - ymin + 1; + //copy inside it + copy_sub_image (this, 0, ymin, xsize, readtop, this, 0, ymax - readtop, TRUE); + } + else + readtop = 0; + ymax = y + bufheight / 2; //new top of image + ymin = ymax - bufheight; //possible bottom + if (ymin < 0) + ymin = 0; //clip to image size + linestoread = ymax - ymin - readtop; + if (lineskip == 0) + failed =::read (fd, (char *) (image + xdim * readtop), + (size_t) (xdim * linestoread)) != xdim * linestoread; + else { + for (failed = FALSE, row = 0; row < linestoread && !failed; row++) { + failed =::read (fd, (char *) (image + (readtop + row) * xdim), + (size_t) xdim) != xdim; + failed |= lseek (fd, lineskip, SEEK_CUR) < 0; + } + } + if (failed) { + READFAILED.error ("IMAGE::bufread", LOG, NULL); + return -1; //read failed + } + if (ymin <= 0) { + close(fd); //finished reading + fd = -1; //not open now + } + return 0; //success +} + + +/********************************************************************** + * write + * + * Write an image to a file in a format determined by the name. + **********************************************************************/ + +INT8 IMAGE::write( //write image + const char *name //name to write + ) { + INT8 type; //type of image + + if (bpp == 0 || image == NULL || bufheight != ysize) + IMAGEUNDEFINED.error ("IMAGE::write", ABORT, NULL); + if (fd >= 0) { + close(fd); //close old file + fd = -1; //no longer open + } + //get image type + type = name_to_image_type (name); + if (type < 0 || imagetypes[type].writer == NULL) { + CANTWRITEIMAGETYPE.error ("IMAGE::write", LOG, name); + return -1; //write not supported + } + #ifdef __UNIX__ + if ((fd = creat (name, 0666)) < 0) + #endif + #ifdef __MSW32__ + if ((fd = open (name, _O_CREAT | _O_WRONLY | _O_BINARY, _S_IWRITE)) < 0) + #endif + #ifdef __MAC__ + if ((fd = creat (name, O_WRONLY | O_BINARY)) < 0) + #endif + { + CANTCREATEFILE.error ("IMAGE::write", LOG, name); + return -1; //failed + } + if (res <= 0) + res = image_default_resolution; + if ((*imagetypes[type].writer) (fd, image, xsize, ysize, bpp, photo_interp, + res) < 0) { + //get header + //write failed + WRITEFAILED.error ("IMAGE::write", LOG, name); + close(fd); + fd = -1; + return -1; //failed + } + return 0; //success +} diff --git a/image/imgio.h b/image/imgio.h new file mode 100644 index 0000000000..5c304517cc --- /dev/null +++ b/image/imgio.h @@ -0,0 +1,22 @@ +/********************************************************************** + * File: imgio.h (Formerly imageio.h) + * Description: Header file for image input/output functions. + * Author: Ray Smith + * Created: Mon Jun 11 12:55:34 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef IMGIO_H +#define IMGIO_H +#endif diff --git a/image/imgs.cpp b/image/imgs.cpp new file mode 100644 index 0000000000..c5d3a7c298 --- /dev/null +++ b/image/imgs.cpp @@ -0,0 +1,1619 @@ +/********************************************************************** + * File: imgs.c (Formerly images.c) + * Description: Main image manipulation functions. + * Author: Ray Smith + * Created: Thu Jun 07 16:25:02 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#ifdef __MSW32__ +#include +#else +#include +#endif +#include +#ifdef __UNIX__ +#include +#endif +#include "stderr.h" +#include "tprintf.h" +#include "imgerrs.h" +#include "memry.h" +#include "imgs.h" +#include "imgio.h" +#include "imgunpk.h" + +#define FIXED_COLOURS 32 /*number of fixed colours */ +#define MIN_4BIT 48 /*4bpp range */ +#define MAX_4BIT 64 +#define MIN_6BIT 64 /*6bpp range */ +#define MAX_6BIT 128 +#define BLACK_PIX 0 + +static UINT8 grey_scales[FIXED_COLOURS] = { + 0, 255, 76, 227, 151, 179, 28, 104, + 149, 72, 215, 67, 53, 44, 156, 137, + 110, 153, 79, 181, 166, 218, 55, 81, + 129, 105, 179, 149, 168, 69, 84, 126 +}; + +#define EXTERN + +EXTERN INT_VAR (image_default_resolution, 300, "Image resolution dpi"); + +/********************************************************************** + * IMAGE + * + * Contructor for an IMAGE class. Makes the image definitely illegal. + **********************************************************************/ + +IMAGE::IMAGE() { //construct an image + bpp = 0; //all illegal + fd = -1; + image = NULL; + photo_interp = 1; + res = image_default_resolution; +} + + +/********************************************************************** + * IMAGE::operator= + * + * Assign an IMAGE to another. The dest becomes the owner of the memory. + **********************************************************************/ + +IMAGE & IMAGE::operator= ( //assignment +IMAGE & source //source image +) { + destroy(); + bpp = source.bpp; + photo_interp = source.photo_interp; + bps = source.bps; + bytespp = (bpp + 7) / 8; + lineskip = source.lineskip; //copy everything + captured = source.captured; + xsize = source.xsize; + ysize = source.ysize; + res = source.res; + image = source.image; + xdim = source.xdim; + bufheight = source.bufheight; + fd = source.fd; + reader = source.reader; + ymin = source.ymin; + ymax = source.ymax; + + source.captured = TRUE; //source now captured + source.fd = -1; + + return *this; +} + + +/********************************************************************** + * create + * + * Create an image (allocate memory) of a specific size and bpp. + **********************************************************************/ + +INT8 IMAGE::create( //get rest of image + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel //bpp required + ) { + UINT8 *pixels; //memory for image + + xdim = check_legal_image_size (x, y, bits_per_pixel); + if (xdim < 0) + return -1; + pixels = (UINT8 *) alloc_big_zeros ((size_t) (xdim * y * sizeof (UINT8))); + if (pixels == NULL) { + MEMORY_OUT.error ("IMAGE::create", ABORT, "Size=(%d,%d)", xdim, y); + return -1; + } + //allocate to image + this->capture (pixels, x, y, bits_per_pixel); + captured = FALSE; + res = image_default_resolution; + return 0; //success +} + + +/********************************************************************** + * destroy + * + * Destroy an image, freeing memory and closing any open file. + **********************************************************************/ + +void IMAGE::destroy() { //get rid of image + if (image != NULL && !captured) { + free_big_mem(image); + } + image = NULL; + if (fd >= 0) { + close(fd); + fd = -1; + } + bpp = 0; +} + + +/********************************************************************** + * capture + * + * Assign a given memory area to an image to use as an image of + * given size and bpp. + **********************************************************************/ + +INT8 IMAGE::capture( //get rest of image + UINT8 *pixels, //image memory + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel //bpp required + ) { + destroy(); + xdim = check_legal_image_size (x, y, bits_per_pixel); + if (xdim < 0) + return -1; + xsize = x; + ysize = y; + bufheight = y; + bpp = bits_per_pixel; + bps = bpp == 24 ? 8 : bpp; + photo_interp = 1; + bytespp = (bpp + 7) / 8; + image = pixels; //assign image area + ymin = 0; + ymax = bufheight; //read it all + captured = TRUE; + res = image_default_resolution; + return 0; //success +} + + +/********************************************************************** + * pixel + * + * Get a single pixel out of the image. + **********************************************************************/ + +UINT8 IMAGE::pixel( //get rest of image + INT32 x, //x coord + INT32 y //y coord + ) { + if (x < 0) + x = 0; //silently clip + else if (x >= xsize) + x = xsize - 1; + if (y < 0) + y = 0; + else if (y >= ysize) + y = ysize - 1; + check_legal_access (x, y, 1); + switch (bpp) { + case 5: + case 6: + case 8: + return image[(ymax - 1 - y) * xdim + x]; + case 4: + return bpp4table[image[(ymax - 1 - y) * xdim + x / 2]][x & 1]; + case 2: + return bpp2table[image[(ymax - 1 - y) * xdim + x / 4]][x & 3]; + case 1: + return bpp1table[image[(ymax - 1 - y) * xdim + x / 8]][x & 7]; + default: + tprintf ("Unexpected bits per pixel %d\n", bpp); + return 0; + } +} + + +/********************************************************************** + * check_legal_image_size + * + * Check that the supplied image sizes are legal. If they are, + * the xdim is returned, else -1. + **********************************************************************/ + +INT32 check_legal_image_size( //get rest of image + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel //bpp required + ) { + if (x <= 0 || y <= 0) { + BADIMAGESIZE.error ("check_legal_image_size", LOG, "(%d,%d)", x, y); + return -1; //failed + } + if (bits_per_pixel != 1 && bits_per_pixel != 2 + && bits_per_pixel != 4 && bits_per_pixel != 5 + && bits_per_pixel != 6 && bits_per_pixel != 8 && bits_per_pixel != 24) { + BADBPP.error ("check_legal_image_size", LOG, "%d", bits_per_pixel); + return -1; + } + //bytes per line + return COMPUTE_IMAGE_XDIM (x, bits_per_pixel); +} + + +/********************************************************************** + * copy_sub_image + * + * Copy a portion of one image to a portion of another image. + * If the bpps are different, the position of the most significant + * bit is preserved. + **********************************************************************/ + +DLLSYM void copy_sub_image( //copy rectangle + IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + BOOL8 adjust_grey //shift to new bpp + ) { + IMAGELINE copyline; //copy of line + UINT8 *copy; //source pointer + INT8 shift; //shift factor + INT32 pixel; //pixel index + INT32 y; //line index + INT32 yoffset; //current adjusted offset + INT32 bytesize; //no of bytes to copy + INT32 srcppb; //pixels per byte + BOOL8 aligned; + + if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0) + return; + if (xext <= 0) + xext = source->xsize; //default to all + if (xext > source->xsize - xstart) + //clip to smallest + xext = source->xsize - xstart; + if (xext > dest->xsize - xdest) + xext = dest->xsize - xdest; + if (yext <= 0) + yext = source->ysize; //default to all + if (yext > source->ysize - ystart) + //clip to smallest + yext = source->ysize - ystart; + if (yext > dest->ysize - ydest) + yext = dest->ysize - ydest; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + srcppb = 8 / source->bpp; //pixels per byte + if (source->bpp == dest->bpp || !adjust_grey) + shift = 0; //no adjustment + else { + shift = source->bps - dest->bps; + if (shift < 0) + shift = -shift; //keep positive + } + aligned = source->bpp == dest->bpp; + if (aligned && srcppb != 0) { + aligned = xstart % srcppb == 0 + && xdest % srcppb == 0 + && (xext % srcppb == 0 || xdest + xext == dest->xsize); + } + for (y = 0; y < yext; y++) { + if (ystart >= ydest) + yoffset = y; //top down + else + yoffset = yext - y - 1; //bottom up + source->check_legal_access (xstart, ystart + yoffset, xext); + dest->check_legal_access (xdest, ydest + yoffset, xext); + if (aligned) { + bytesize = COMPUTE_IMAGE_XDIM (xext, source->bpp); + //get bytes per line + if (srcppb == 0) + //do cheap move + memmove (dest->image + (dest->ymax - 1 - ydest - yoffset) * dest->xdim + xdest * 3, source->image + (source->ymax - 1 - ystart - yoffset) * source->xdim + xstart * 3, (unsigned) bytesize); + else + //do cheap move + memmove (dest->image + (dest->ymax - 1 - ydest - yoffset) * dest->xdim + xdest / srcppb, source->image + (source->ymax - 1 - ystart - yoffset) * source->xdim + xstart / srcppb, (unsigned) bytesize); + } + else { + if (shift == 0) { + source->fast_get_line (xstart, ystart + yoffset, xext, + ©line); + } + else if (source->bpp < dest->bpp) { + source->get_line (xstart, ystart + yoffset, xext, ©line, 0); + if (source->bpp <= shift + && (source->bpp == 1 || source->bpp == 4)) { + if (source->bpp == 1) { + for (pixel = 0, copy = copyline.pixels; pixel < xext; + pixel++, copy++) + if (*copy) + *copy = 0xff; + } + else { + for (pixel = 0, copy = copyline.pixels; pixel < xext; + pixel++, copy++) + //scale up + *copy = (*copy << shift) | *copy; + } + } + else { + for (pixel = 0, copy = copyline.pixels; pixel < xext; + pixel++) + *copy++ <<= shift; //scale up + } + } + else { + source->get_line (xstart, ystart + yoffset, xext, ©line, 0); + if (source->bpp == 24) { + for (pixel = 0, copy = copyline.pixels + 1; pixel < xext; + pixel++) { + *copy >>= shift; + copy += 3; + } + } + else { + for (pixel = 0, copy = copyline.pixels; pixel < xext; + pixel++) + *copy++ >>= shift; //scale down + } + } + dest->put_line (xdest, ydest + yoffset, xext, ©line, 0); + } + } +} + + +/********************************************************************** + * enlarge_sub_image + * + * Enlarge a portion of one image to a portion of another image. + * If the bpps are different, the position of the most significant + * bit is preserved. + **********************************************************************/ + +DLLSYM void enlarge_sub_image( //enlarge rectangle + IMAGE *source, //source image + INT32 xstart, //scaled start coords + INT32 ystart, + IMAGE *dest, //destination image + INT32 xdest, //dest coords + INT32 ydest, + INT32 xext, //destination extent + INT32 yext, + INT32 scale, //scale factor + BOOL8 adjust_grey //shift to new bpp + ) { + INT8 shift; //shift factor + UINT8 pixel; //current pixel + INT32 srcext; //source extent + INT32 xoffset; //column index + INT32 yoffset; //line index + INT32 xindex, yindex; //index in super pixel + INT32 startxindex; //initial x index + INT32 xscale; //x scale factor + UINT8 *src; //source pixels + UINT8 *destpix; //dest pixels + IMAGELINE copyline; //copy of line + IMAGELINE bigline; //expanded line + + if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0) + return; + + if (xext <= 0) + xext = dest->xsize; //default to all + if (xext > source->xsize * scale - xstart) + //clip to smallest + xext = source->xsize * scale - xstart; + if (xext > dest->xsize - xdest) + xext = dest->xsize - xdest; + if (yext <= 0) + yext = dest->ysize; //default to all + if (yext > source->ysize * scale - ystart) + yext = source->ysize * scale - ystart; + if (yext > dest->ysize - ydest) + yext = dest->ysize - ydest; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + xindex = xstart % scale; //offset in super pixel + startxindex = xindex; + yindex = ystart % scale; + //no of source pixels + srcext = (xext + xindex + scale - 1) / scale; + xstart /= scale; //actual start + ystart /= scale; + if (adjust_grey) { + shift = dest->bps - source->bps; + } + else + shift = 0; //no adjustment + bigline.init (xext * 3); + bigline.bpp = dest->bpp == 24 ? source->bpp : dest->bpp; + + for (yoffset = 0; yoffset < yext; ystart++) { + source->check_legal_access (xstart, ystart, srcext); + dest->check_legal_access (xdest, ydest + yoffset, xext); + source->fast_get_line (xstart, ystart, srcext, ©line); + src = copyline.pixels; + destpix = bigline.pixels; + xscale = scale; //enlargement factor + if (source->bpp == 24 && dest->bpp == 24) { + for (xoffset = 0, xindex = startxindex; xoffset < xext; + src += source->bytespp) { + xoffset += xscale - xindex; + if (xoffset > xext) + xscale -= xoffset - xext; + for (; xindex < xscale; xindex++) { + *destpix++ = *src; + *destpix++ = *(src + 1); + *destpix++ = *(src + 2); + } + xindex = 0; + } + } + else { + if (source->bpp == 24) + src++; + for (xoffset = 0, xindex = startxindex; xoffset < xext; + src += source->bytespp) { + xoffset += xscale - xindex; + if (xoffset > xext) + //clip to dest limit + xscale -= xoffset - xext; + if (shift == 0) + pixel = *src; + else if (shift > 0) + pixel = *src << shift; + else + pixel = *src >> (-shift); + for (; xindex < xscale; xindex++) + *destpix++ = pixel; //duplicate pixel + xindex = 0; + } + } + for (; yoffset < yext && yindex < scale; yindex++, yoffset++) { + dest->put_line (xdest, ydest + yoffset, xext, &bigline, 0); + } + yindex = 0; + } +} + + +/********************************************************************** + * fast_reduce_sub_image + * + * Reduce a portion of one image to a portion of another image. + * If the bpps are different, the position of the most significant + * bit is preserved. + * This is a fast but dirty version, which simply sub-samples. + * It does not smooth as it reduces. + **********************************************************************/ + +DLLSYM void fast_reduce_sub_image( //reduce rectangle + IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //reduction factor + BOOL8 adjust_grey //shift to new bpp + ) { + INT8 shift; //shift factor + INT32 xfactor; //run on x coord + INT32 divisor; //total cell area + INT32 xindex, yindex; //into averaging square + INT32 xcoord; //current x coord + INT32 destext; //destination size + INT32 yoffset; //current adjusted offset + UINT8 *pixel; //ptr to source pixels + INT32 *sums; //ptr to sums array + IMAGELINE copyline; //copy of line + INT32 *linesums; //averaging sums + + if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0) + return; + if (xext <= 0) + xext = source->xsize; //default to all + if (xext > source->xsize - xstart) + //clip to smallest + xext = source->xsize - xstart; + if (xext > (dest->xsize - xdest) * scale) + xext = (dest->xsize - xdest) * scale; + if (yext <= 0) + yext = source->ysize; //default to all + if (yext > source->ysize - ystart) + //clip to smallest + yext = source->ysize - ystart; + if (yext > (dest->ysize - ydest) * scale) + yext = (dest->ysize - ydest) * scale; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + xfactor = xext % scale; //left overs + if (xfactor == 0) + xfactor = scale; + //destination pixels + destext = (xext + scale - 1) / scale; + if (adjust_grey) + //shift factor + shift = dest->bps - source->bps; + else + shift = 0; //no adjustment + linesums = new INT32[destext * source->bytespp]; + + for (yoffset = 0; yoffset < yext; ydest++) { + source->check_legal_access (xstart, ystart + yoffset, xext); + dest->check_legal_access (xdest, ydest, destext); + for (xindex = destext * source->bytespp - 1; xindex >= 0; xindex--) + linesums[xindex] = 0; //zero sums + for (yindex = 0; yindex < scale + && ystart + yoffset < source->ysize; yindex += 3) { + source->fast_get_line (xstart, ystart + yoffset, xext, ©line); + pixel = copyline.pixels; //start of line + if (source->bpp == 24) { + for (xcoord = 1, sums = linesums; xcoord < destext; + xcoord++, sums += 3) { + for (xindex = 0; xindex < scale; xindex += 2) { + *sums += *pixel++; + *(sums + 1) += *pixel++; + *(sums + 2) += *pixel++; + pixel += 3; + } + if (scale & 1) + pixel -= 3; //correct position + } + for (xindex = 0; xindex < xfactor; xindex += 2) { + *sums += *pixel++; + *(sums + 1) += *pixel++; + *(sums + 2) += *pixel++; + pixel += 3; + } + } + else { + for (xcoord = 1, sums = linesums; xcoord < destext; + xcoord++, sums++) { + for (xindex = 0; xindex < scale; xindex += 2) { + *sums += *pixel; + pixel += 2; + } + if (scale & 1) + pixel--; //correct position + } + for (xindex = 0; xindex < xfactor; xindex += 2) { + *sums += *pixel; + pixel += 2; + } + } + yoffset += 3; //every 3 lines + } + if (yindex > scale) + yoffset -= yindex - scale; //back on right scale + copyline.init (); //set pixels back to array + copyline.bpp = source->bpp; + pixel = copyline.pixels; + //pixels in block + divisor = ((yindex + 2) / 3) * ((scale + 1) / 2); + if (shift <= 0) { + divisor <<= (-shift); //do greyscale correction + for (sums = linesums, xindex = (destext - 1) * source->bytespp; + xindex > 0; xindex--) + //turn to destination value + *pixel++ = (UINT8) (*sums++ / divisor); + for (xindex = source->bytespp; xindex > 0; xindex--) + *pixel++ = *sums++ + / (((yindex + 2) / 3) * ((xfactor + 1) / 2) << (-shift)); + //lastone different + } + else { + for (sums = linesums, xindex = (destext - 1) * source->bytespp; + xindex > 0; xindex--) + *pixel++ = (UINT8) ((*sums++ << shift) / divisor); + //destination value + for (xindex = source->bytespp; xindex > 0; xindex--) + //last one different + *pixel++ = (*(sums++) << shift) / (((yindex + 2) / 3) * ((xfactor + 1) / 2)); + } + //put in destination + dest->put_line (xdest, ydest, destext, ©line, 0); + } + delete linesums; +} + + +/********************************************************************** + * reduce_sub_image + * + * Reduce a portion of one image to a portion of another image. + * If the bpps are different, the position of the most significant + * bit is preserved. + **********************************************************************/ + +DLLSYM void reduce_sub_image( //reduce rectangle + IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //reduction factor + BOOL8 adjust_grey //shift to new bpp + ) { + INT8 shift; //shift factor + INT32 xfactor; //run on x coord + INT32 divisor; //total cell area + INT32 div2; //total cell area divided by 2 + INT32 xindex, yindex; //into averaging square + INT32 xcoord; //current x coord + INT32 destext; //destination size + INT32 yoffset; //current adjusted offset + UINT8 *pixel; //ptr to source pixels + INT32 *sums; //ptr to sums array + IMAGELINE copyline; //copy of line + INT32 *linesums; //averaging sums + + if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0) + return; + if (xext <= 0) + xext = source->xsize; //default to all + if (xext > source->xsize - xstart) + //clip to smallest + xext = source->xsize - xstart; + if (xext > (dest->xsize - xdest) * scale) + xext = (dest->xsize - xdest) * scale; + if (yext <= 0) + yext = source->ysize; //default to all + if (yext > source->ysize - ystart) + //clip to smallest + yext = source->ysize - ystart; + if (yext > (dest->ysize - ydest) * scale) + yext = (dest->ysize - ydest) * scale; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + xfactor = xext % scale; //left overs + if (xfactor == 0) + xfactor = scale; + //destination pixels + destext = (xext + scale - 1) / scale; + if (adjust_grey) + //shift factor + shift = dest->bps - source->bps; + else + shift = 0; //no adjustment + linesums = new INT32[destext * source->bytespp]; + + for (yoffset = 0; yoffset < yext; ydest++) { + source->check_legal_access (xstart, ystart + yoffset, xext); + dest->check_legal_access (xdest, ydest, destext); + for (xindex = 0; xindex < (destext) * source->bytespp; xindex++) + linesums[xindex] = 0; //zero sums + for (yindex = 0; yindex < scale && ystart + yoffset < source->ysize; + yindex++) { + source->fast_get_line (xstart, ystart + yoffset, xext, ©line); + pixel = copyline.pixels; //start of line + if (source->bpp == 24) { + for (xcoord = 1, sums = linesums; xcoord < destext; + xcoord++, sums += 3) { + for (xindex = 0; xindex < scale; xindex++) { + *sums += *pixel++; + *(sums + 1) += *pixel++; + *(sums + 2) += *pixel++; + } + } + for (xindex = 0; xindex < xfactor; xindex++) { + *sums += *pixel++; + *(sums + 1) += *pixel++; + *(sums + 2) += *pixel++; + } + } + else { + for (xcoord = 1, sums = linesums; xcoord < destext; + xcoord++, sums++) { + for (xindex = 0; xindex < scale; xindex++) + *sums += *pixel++; + } + for (xindex = 0; xindex < xfactor; xindex++) + *sums += *pixel++; + } + yoffset++; //next line + } + copyline.init (); //set pixels back to array + copyline.set_bpp (source->bpp); + pixel = copyline.pixels; + divisor = yindex * scale; + if (divisor == 0) { + tprintf + ("Impossible:divisor=0!, yindex=%d, scale=%d, yoffset=%d,yext=%d\n", + yindex, scale, yoffset, yext); + break; + } + if (shift <= 0) { + divisor <<= (-shift); //do greyscale correction + div2 = divisor / 2; + for (sums = linesums, xindex = (destext - 1) * source->bytespp; + xindex > 0; xindex--) + *pixel++ = (UINT8) ((div2 + *sums++) / divisor); + //turn to destination value + div2 = (yindex * xfactor << (-shift)) / 2; + for (xindex = source->bytespp; xindex > 0; xindex--) + *pixel++ = + (UINT8) ((div2 + *sums++) / (yindex * xfactor << (-shift))); + //lastone different + } + else { + div2 = divisor / 2; + for (sums = linesums, xindex = (destext - 1) * source->bytespp; + xindex > 0; xindex--) + *pixel++ = (UINT8) ((div2 + (*sums++ << shift)) / divisor); + //destination value + div2 = (yindex * xfactor) / 2; + for (xindex = source->bytespp; xindex > 0; xindex--) + *pixel++ = + (UINT8) ((div2 + (*sums++ << shift)) / (yindex * xfactor)); + //last one different + } + //put in destination + dest->put_line (xdest, ydest, destext, ©line, 0); + } + delete linesums; +} + + +/********************************************************************** + * invert_image + * + * Invert the given image (the slow way.) + **********************************************************************/ + +DLLSYM void invert_image( /*invert the image */ + IMAGE *image /*image ot invert */ + ) { + UINT8 mask; //bit mask + UINT8 bytespp; //bytes per pixel + INT32 xsize, ysize; /*size of image */ + INT32 xindex, yindex; /*index into image */ + UINT8 *pixel; /*current pixel */ + IMAGELINE line; /*line of image */ + + bytespp = image->get_bpp () == 24 ? 3 : 1; + xsize = image->get_xsize (); /*find sizes */ + ysize = image->get_ysize (); + //pixel mask + mask = (1 << image->get_bpp ()) - 1; + /*do each line */ + for (yindex = ysize - 1; yindex >= 0; yindex--) { + image->fast_get_line (0, yindex, xsize, &line); + for (pixel = line.pixels, xindex = xsize * bytespp; xindex > 0; + xindex--) { + *pixel = (*pixel) ^ mask; //invert image only + ++pixel; + } + /*put it back */ + image->fast_put_line (0, yindex, xsize, &line); + } +} + + +/********************************************************************** + * bias_sub_image + * + * Add a constant to a portion of an image. + **********************************************************************/ + +DLLSYM void bias_sub_image( //bias rectangle + IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + UINT8 bias //number to add + ) { + IMAGELINE copyline; //copy of line + UINT8 *copy; //source pointer + INT32 pixel; //pixel index + INT32 y; //line index + UINT8 bytespp; //bytes per pixel + + if (xstart < 0 || ystart < 0) + return; + if (xext <= 0) + xext = source->get_xsize (); //default to all + if (xext > source->get_xsize () - xstart) + //clip to smallest + xext = source->get_xsize () - xstart; + if (yext <= 0) + yext = source->get_ysize (); //default to all + if (yext > source->get_ysize () - ystart) + //clip to smallest + yext = source->get_ysize () - ystart; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + bytespp = source->get_bpp () == 24 ? 3 : 1; + for (y = 0; y < yext; y++) { + source->check_legal_access (xstart, ystart + y, xext); + source->fast_get_line (xstart, ystart + y, xext, ©line); + for (pixel = xext * bytespp, copy = copyline.pixels; pixel > 0; + pixel--, copy++) + *copy += bias; //add bias + + source->fast_put_line (xstart, ystart + y, xext, ©line); + } +} + + +/********************************************************************** + * starbase_to_normal + * + * Copy a portion of one image to a portion of another image. + * This function maps the colour tables used on the screen to + * greyscale values in the way "normally" expected. + **********************************************************************/ + +DLLSYM void starbase_to_normal( //copy rectangle + IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + BOOL8 preserve_grey //shift to new bpp + ) { + IMAGELINE copyline; //copy of line + UINT8 *copy; //source pointer + INT8 shift4; //shift factor + INT8 shift6; //shift factor + INT8 colour_shift; //shift of colours + UINT8 white_level; //dest white value + INT32 pixel; //pixel index + INT32 y; //line index + INT32 yoffset; //current adjusted offset + INT8 srcppb; //pixels per byte + + if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0) + return; + if (xext <= 0) + xext = source->get_xsize (); //default to all + if (xext > source->get_xsize () - xstart) + //clip to smallest + xext = source->get_xsize () - xstart; + if (xext > dest->get_xsize () - xdest) + xext = dest->get_xsize () - xdest; + if (yext <= 0) + yext = source->get_ysize (); //default to all + if (yext > source->get_ysize () - ystart) + //clip to smallest + yext = source->get_ysize () - ystart; + if (yext > dest->get_ysize () - ydest) + yext = dest->get_ysize () - ydest; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + //pixels per byte + srcppb = 8 / source->get_bpp (); + shift4 = 4 - dest->get_bpp (); //for different bpps + shift6 = 6 - dest->get_bpp (); + //for grey preserve + colour_shift = 8 - dest->get_bpp (); + white_level = dest->get_white_level (); + for (y = 0; y < yext; y++) { + if (ystart >= ydest) + yoffset = y; //top down + else + yoffset = yext - y - 1; //bottom up + source->check_legal_access (xstart, ystart + yoffset, xext); + dest->check_legal_access (xdest, ydest + yoffset, xext); + source->get_line (xstart, ystart + yoffset, xext, ©line, 0); + for (pixel = 0, copy = copyline.pixels; pixel < xext; pixel++) { + if (*copy < FIXED_COLOURS && preserve_grey) + *copy = grey_scales[*copy] >> colour_shift; + else if (*copy < FIXED_COLOURS) { + if (*copy == BLACK_PIX) + *copy = white_level; //black->white + else + *copy = 0; //others->black + } + else if (*copy >= MIN_4BIT && *copy < MAX_4BIT) { + if (shift4 < 0) + *copy = (*copy - MIN_4BIT) << (-shift4); + else + *copy = (*copy - MIN_4BIT) >> shift4; + } + else if (*copy >= MIN_6BIT && *copy < MAX_6BIT) { + if (shift6 < 0) + *copy = (*copy - MIN_6BIT) << (-shift6); + else + *copy = (*copy - MIN_6BIT) >> shift6; + } + else { + *copy = white_level; //white the rest + } + copy++; + } + dest->put_line (xdest, ydest + yoffset, xext, ©line, 0); + } +} + + +/********************************************************************** + * fast_get_line + * + * Get a line of image into the supplied image line buffer. + * The image is converted to 8bpp by simple assignment. + * If the image is aleady 8 or 6bpp, no copy is done and a pointer + * to the correct image section is put in the line buffer. + **********************************************************************/ + +void IMAGE::fast_get_line( //get image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf //line to copy to + ) { + if (width > 0 && bpp > 4) { + check_legal_access(x, y, width); + //get pointer only + linebuf->pixels = image + xdim * (ymax - 1 - y) + x * bytespp; + } + else + //just copy it + this->get_line (x, y, width, linebuf, 0); + linebuf->bpp = bpp; +} + + +/********************************************************************** + * get_line + * + * Get a line of image into the supplied image line buffer. + * The image is converted to 8bpp by simple assignment. + **********************************************************************/ + +void IMAGE::get_line( //get image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins //size of margins + ) { + UINT8 *src; //source pointer + UINT8 *dest; //destination pointer + UINT8 *unpacksrc; //unpacking pointer + INT8 bit; //bit index + INT8 pixperbyte; //pixels per byte + UINT8 white; //white colour + INT32 pixel; //pixel index + + //test coords + this->check_legal_access (x, y, width); + if (width > xsize - x) + width = xsize - x; //clip to image + width *= bytespp; + linebuf->init (width + margins * bytespp * 2); + linebuf->bpp = bpp; + //start of line + src = image + xdim * (ymax - 1 - y); + dest = linebuf->line; //destination line + linebuf->pixels = dest; + white = (1 << bpp) - 1; //max value of pixel + for (pixel = margins * bytespp; pixel > 0; pixel--) { + *dest++ = white; //margins are white + } + if (width > 0) { + if (bpp > 4) { + src += x; //offset + //easy way + memmove (dest, src, (unsigned) width); + } + else if (bpp == 4) { + src += x / 2; //offset on line + if (x & 1) { + //get coded nibble + *dest++ = bpp4table[*src++][1]; + width--; + } + while (width >= 2) { + //get coded bits + unpacksrc = bpp4table[*src++]; + *dest++ = *unpacksrc++; + *dest++ = *unpacksrc++; //copy nibbles + width -= 2; + } + if (width) { + //get coded nibble + *dest++ = bpp4table[*src++][0]; + } + } + else if (bpp == 2) { + pixperbyte = 4; + src += x / 4; //offset on line + bit = (INT8) (x % 4); //offset in byte + width += bit; + while (width > 0) { //until all done + if (width < pixperbyte) + //less on last byte + pixperbyte = (INT8) width; + //get coded bits + unpacksrc = &bpp2table[*src++][bit]; + for (; bit < pixperbyte; bit++) + *dest++ = *unpacksrc++;//copy bytes + width -= pixperbyte; + bit = 0; + } + } + else { + pixperbyte = 8; + src += x / 8; //offset on line + bit = (INT8) (x % 8); //offset in byte + width += bit; + while (width > 0) { //until all done + if (width < pixperbyte) + //less on last byte + pixperbyte = (INT8) width; + //get coded bits + unpacksrc = &bpp1table[*src++][bit]; + for (; bit < pixperbyte; bit++) + *dest++ = *unpacksrc++;//copy bytes + width -= pixperbyte; + bit = 0; + } + } + } + for (pixel = margins * bytespp; pixel > 0; pixel--) { + *dest++ = white; //margins are white + } +} + + +/********************************************************************** + * get_column + * + * Get a column of image into the supplied image line buffer. + * The image is converted to 8bpp by simple assignment. + **********************************************************************/ + +void IMAGE::get_column( //get image column + INT32 x, //coord to start at + INT32 y, //line to get + INT32 height, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins //size of margins + ) { + UINT8 *src; //source pointer + UINT8 *dest; //destination pointer + INT8 bit; //bit index + INT8 pixperbyte; //pixels per byte + UINT8 white; //white colour + INT32 pixel; //pixel index + + //test coords + this->check_legal_access (x, y, 1); + //test coords + this->check_legal_access (x, y + height - 1, 1); + if (height > ysize - y) + height = ysize - y; //clip to image + linebuf->init (height * bytespp + margins * bytespp * 2); + //start of line + src = image + xdim * (ymax - 1 - y); + dest = linebuf->line; //destination line + linebuf->pixels = dest; + white = (1 << bpp) - 1; //max value of pixel + for (pixel = margins * bytespp; pixel > 0; pixel--) { + *dest++ = white; //margins are white + } + if (height > 0) { + if (bpp == 24) { + src += x * bytespp; //offset + for (; height > 0; --height) { + *dest++ = *src; //copy bytes + *dest++ = *(src + 1); + *dest++ = *(src + 2); + src -= xdim; + } + } + else if (bpp > 4) { + src += x; + for (; height > 0; --height) { + *dest++ = *src; //copy bytes + src -= xdim; + } + } + else if (bpp == 4) { + src += x / 2; //offset on line + if (x & 1) { + for (; height > 0; --height) { + //get coded nibble + *dest++ = bpp4table[*src][1]; + src -= xdim; + } + } + else { + for (; height > 0; --height) { + //get coded nibble + *dest++ = bpp4table[*src][0]; + src -= xdim; + } + } + } + else if (bpp == 2) { + pixperbyte = 4; + src += x / 4; //offset on line + bit = (INT8) (x % 4); //offset in byte + for (; height > 0; --height) { + //get coded bits + *dest++ = bpp2table[*src][bit]; + src -= xdim; + } + } + else { + pixperbyte = 8; + src += x / 8; //offset on line + bit = (INT8) (x % 8); //offset in byte + for (; height > 0; --height) { + //get coded bits + *dest++ = bpp1table[*src][bit]; + src -= xdim; + } + } + } + for (pixel = margins * bytespp; pixel > 0; pixel--) { + *dest++ = white; //margins are white + } +} + + +/********************************************************************** + * fast_put_line + * + * Put a line buffer back into the image. + * If the line buffer merely points back into the image, nothing is done. + * Otherwise, put_line is used to copy the line back. + **********************************************************************/ + +void IMAGE::fast_put_line( //put image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to put + IMAGELINE *linebuf //line to copy to + ) { + if (width > 0 && (bpp <= 4 || linebuf->pixels == linebuf->line)) + //just copy it + put_line (x, y, width, linebuf, 0); +} + + +/********************************************************************** + * put_line + * + * Put the supplied line buffer into the image. + * The image is converted from 8bpp by simple assignment. + **********************************************************************/ + +void IMAGE::put_line( //put image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins //margins in buffer + ) { + UINT8 *src; //source pointer + UINT8 *dest; //destination pointer + INT8 bit; //bit index + UINT8 pixel; //collected bits + INT8 pixperbyte; //pixels in a byte + INT8 bytesperpix; //in source + + this->check_legal_access (x, y, width); + if (width > xsize - x) + width = xsize - x; //clip to image + if (width <= 0) + return; //nothing to do + //source line + src = linebuf->pixels + margins; + //start of line + dest = image + xdim * (ymax - 1 - y); + + if (linebuf->bpp == 24) { + src++; + bytesperpix = 3; + } + else + bytesperpix = 1; + if (bpp == 24 && linebuf->bpp == 24) { + dest += x * bytespp; + width *= bytespp; + memmove (dest, src - 1, (unsigned) width); + } + else if (bpp == 24) { + src--; + dest += x * bytespp; + while (width > 0) { + pixel = *src++; + *dest++ = pixel; + *dest++ = pixel; + *dest++ = pixel; + width--; + } + } + else if (bpp > 4) { + dest += x; //offset + if (linebuf->bpp == 24) { + while (width > 0) { + *dest++ = *src; + src += 3; + width--; + } + } + else + //easy way + memmove (dest, src, (unsigned) width); + } + else if (bpp == 4) { + dest += x / 2; //offset on line + if (x & 1) { + *dest &= 0xf0; //clean odd byte + *dest++ |= *src & 0x0f; //and copy it + src += bytesperpix; + width--; + } + while (width >= 2) { + pixel = *src << 4; //left pixel + src += bytesperpix; + pixel |= *src & 0x0f; //right pixel + src += bytesperpix; + *dest++ = pixel; + width -= 2; + } + if (width) { + *dest &= 0x0f; //clean odd byte + *dest |= *src << 4; + } + } + else if (bpp == 2) { + pixperbyte = 4; + dest += x / 4; //offset on line + bit = (INT8) (x % 4); //offset in byte + width += bit; + pixel = *dest >> (8 - bit - bit); + while (width >= 4) { //until all done + for (; bit < 4; bit++) { + pixel <<= 2; //make space for new one + pixel |= *src & 3; + src += bytesperpix; + } + *dest++ = pixel; //new pixel + width -= 4; + bit = 0; + } + if (width > 0) { //until all done + for (bit = 0; bit < width; bit++) { + pixel <<= 2; //make space for new one + pixel |= *src & 3; + src += bytesperpix; + } + pixel <<= (8 - bit - bit); //shift rest + //keep trainling bits + pixel |= *dest & ((1 << (8 - bit - bit)) - 1); + *dest++ = pixel; //new pixel + } + } + else { + pixperbyte = 8; + dest += x / 8; //offset on line + bit = (INT8) (x % 8); //offset in byte + width += bit; + pixel = *dest >> (8 - bit); + while (width >= 8) { //until all done + for (; bit < 8; bit++) { + pixel <<= 1; //make space for new one + pixel |= *src & 1; + src += bytesperpix; + } + *dest++ = pixel; //new pixel + width -= 8; + bit = 0; + } + width -= bit; + if (width > 0) { //until all done + while (width > 0) { + pixel <<= 1; //make space for new one + pixel |= *src & 1; + src += bytesperpix; + bit++; + width--; + } + pixel <<= (8 - bit); //shift rest + //keep trainling bits + pixel |= *dest & ((1 << (8 - bit)) - 1); + *dest++ = pixel; //new pixel + } + } +} + + +/********************************************************************** + * put_column + * + * Put the supplied column buffer into the image. + * The image is converted from 8bpp by simple assignment. + **********************************************************************/ + +void IMAGE::put_column( //put image column + INT32 x, //coord to start at + INT32 y, //line to get + INT32 height, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins //margins in buffer + ) { + UINT8 *src; //source pointer + UINT8 *dest; //destination pointer + INT8 bit; //bit index + UINT8 pixel; //collected bits + INT8 bytesperpix; //in source + + this->check_legal_access (x, y, 1); + this->check_legal_access (x, y + height - 1, 1); + if (height > ysize - y) + height = ysize - y; //clip to image + if (height <= 0) + return; //nothing to do + //source line + src = linebuf->pixels + margins; + //start of line + dest = image + xdim * (ymax - 1 - y); + + if (linebuf->bpp == 24) { + src++; + bytesperpix = 3; + } + else + bytesperpix = 1; + + if (bpp == 24 && linebuf->bpp == 24) { + dest += x * bytesperpix; + src--; + for (; height > 0; --height) { + *dest = *src++; + *(dest + 1) = *src++; + *(dest + 2) = *src++; + dest -= xdim; + } + } + else if (bpp == 24) { + src--; + dest += x * bytesperpix; + for (; height > 0; --height) { + pixel = *src++; + *dest = pixel; + *(dest + 1) = pixel; + *(dest + 2) = pixel; + dest -= xdim; + } + } + else if (bpp > 4) { + dest += x; //offset + for (; height > 0; --height) { + *dest = *src; + src += bytesperpix; + dest -= xdim; + } + } + else if (bpp == 4) { + dest += x / 2; //offset on line + if (x & 1) { + for (; height > 0; --height) { + *dest &= 0xf0; //clean odd byte + *dest |= *src & 0x0f; //and copy it + src += bytesperpix; + dest -= xdim; + } + } + else { + for (; height > 0; --height) { + *dest &= 0x0f; //clean odd byte + *dest |= *src << 4; + src += bytesperpix; + dest -= xdim; + } + } + } + else if (bpp == 2) { + dest += x / 4; //offset on line + bit = (INT8) (x % 4); //offset in byte + bit = 6 - bit - bit; //bit shift + pixel = ~(3 << bit); //mask + for (; height > 0; --height) { + //change 2 bits + *dest = (*dest & pixel) | ((*src & 3) << bit); + src += bytesperpix; + dest -= xdim; + } + } + else { + dest += x / 8; //offset on line + bit = (INT8) (x % 8); //offset in byte + bit = 7 - bit; + pixel = ~(1 << bit); + for (; height > 0; --height) { + //change 1 bit + *dest = (*dest & pixel) | ((*src & 1) << bit); + src += bytesperpix; + dest -= xdim; + } + } +} + + +/********************************************************************** + * check_legal_access + * + * Check that x,y are within the bounds of the image. + * Call bufread if necessary to get the image into memory. + **********************************************************************/ + +void IMAGE::check_legal_access( //check coords are legal + INT32 x, //coords to check + INT32 y, + INT32 xext //xextent + ) { + if (x < 0 || x >= xsize || y < 0 || y >= ysize || x + xext > xsize) + BADIMAGECOORDS.error ("IMAGE::check_legal_access", + ABORT, "(%d+%d,%d)", x, xext, y); + if (y >= ymax) + BADIMAGESEEK.error ("IMAGE::check_legal_access", ABORT, "(%d,%d)", x, y); + if (y < ymin) + bufread(y); //read some more +} + + +/************************************************************************* + * convolver() + * + * Calls the specified function for each pixel in the image, passing in an m x n + * window of the image, centred on the pixel. The convolution function returns + * a new value for the pixel, based on the window. + * + * At the edges of the image, the window is padded to white pixels. + *************************************************************************/ + +void +IMAGE::convolver ( //Map fn over window +INT32 win_width, //Window width +INT32 win_height, //Window height +void (*convolve) ( //Conv Function +UINT8 ** pixels, //Of window +UINT8 bytespp, //1 or 3 for colour +INT32 win_wd, //Window width +INT32 win_ht, //Window height +UINT8 ret_white_value, //White value to RETURN +UINT8 * result) //Ptr to result pix +) { + IMAGELINE new_row; //Replacement pixels + IMAGELINE *old_rows; //Rows being processed + INT32 oldest_imline; //Next imline to replace + UINT8 **window; //ptrs to pixel rows + UINT8 **winmax; //ptrs to pixel rows + UINT8 **win; //ptrs to pixel rows + INT32 current_row; //Row being calculated + INT32 current_col; //Col being calculated + INT32 row = 0; //Next row to get + + INT32 i, j; + UINT8 *pix; + UINT8 *max; + INT32 xmargin = win_width / 2; + INT32 ymargin = win_height / 2; + UINT8 white = get_white_level (); + const UINT8 max_white = 255; + float white_scale = (float) 255 / get_white_level (); + + if (((win_width % 2) == 0) || + ((win_height % 2) == 0) || + (win_height < 3) || + (win_width < 3) || (win_height > ysize / 2) || (win_width > xsize / 2)) + BADWINDOW.error ("IMAGE::convolver", + ABORT, "(%d x %d)", win_width, win_height); + + new_row.init (xsize * bytespp); + new_row.set_bpp (bpp); + old_rows = new IMAGELINE[win_height]; + for (i = 0; i < win_height; i++) { + old_rows[i].init ((xsize + 2 * xmargin) * bytespp); + old_rows[i].set_bpp (bpp); + } + + window = (UINT8 **) alloc_mem (win_height * sizeof (UINT8 *)); + winmax = window + win_height; + + /* Make bottom border */ + for (oldest_imline = 0; oldest_imline < ymargin; oldest_imline++) { + pix = old_rows[oldest_imline].pixels; + max = pix + (xsize + 2 * xmargin) * bytespp; + while (pix < max) + *pix++ = max_white; + } + /* Initialise remaining rows but one*/ + for (; oldest_imline < win_height - 1; oldest_imline++) { + get_line (0, row++, xsize, &old_rows[oldest_imline], xmargin); + if (max_white != white) { + pix = old_rows[oldest_imline].pixels; + max = pix + (xsize + 2 * xmargin) * bytespp; + while (pix < max) { + *pix = (UINT8) (*pix * white_scale); + ++pix; + } + } + } + + /* Image Processing */ + + for (current_row = 0; current_row < ysize;) { + /* Get next row and re-initialise window array */ + if (row < ysize) { + get_line (0, row++, xsize, &old_rows[oldest_imline], xmargin); + if (max_white != white) { + pix = old_rows[oldest_imline].pixels; + max = pix + (xsize + 2 * xmargin) * bytespp; + while (pix < max) { + *pix = (UINT8) (*pix * white_scale); + ++pix; + } + } + } + else { + pix = old_rows[oldest_imline].pixels; + max = pix + (xsize + 2 * xmargin) * bytespp; + while (pix < max) + *pix++ = max_white; + } + oldest_imline++; + if (oldest_imline >= win_height) + oldest_imline = 0; + + /* Process line */ + pix = new_row.pixels; + for (current_col = 0; current_col < xsize;) { + /* Set up window ptrs */ + if (current_col == 0) { + j = oldest_imline; + for (i = 0; i < win_height; i++) { + window[i] = old_rows[j++].pixels; + if (j >= win_height) + j = 0; + } + } + else { + for (win = window; win < winmax; (*win++) += bytespp); + //Move along rows + } + + convolve(window, bytespp, win_width, win_height, white, pix); + pix += bytespp; + current_col++; + } + + put_line (0, current_row, xsize, &new_row, 0); + new_row.init (); + new_row.set_bpp (bpp); + current_row++; + } +} diff --git a/image/imgs.h b/image/imgs.h new file mode 100644 index 0000000000..e023dc7257 --- /dev/null +++ b/image/imgs.h @@ -0,0 +1,102 @@ +/********************************************************************** + * File: imgs.h (Formerly images.h) + * Description: Header file for IMAGE member functions. + * Author: Ray Smith + * Created: Mon Jun 11 15:32:53 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef IMGS_H +#define IMGS_H + +#include "img.h" +#include "varable.h" + +extern INT_VAR_H (image_default_resolution, 300, "Image resolution dpi"); + +INT32 check_legal_image_size( //get rest of image + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel //bpp required + ); + //copy rectangle +extern DLLSYM void copy_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + BOOL8 adjust_grey //shift to new bpp + ); + //enlarge rectangle +extern DLLSYM void enlarge_sub_image(IMAGE *source, //source image + INT32 xstart, //scaled start coords + INT32 ystart, + IMAGE *dest, //destination image + INT32 xdest, //dest coords + INT32 ydest, + INT32 xext, //destination extent + INT32 yext, + INT32 scale, //scale factor + BOOL8 adjust_grey //shift to new bpp + ); + //reduce rectangle +extern DLLSYM void fast_reduce_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //reduction factor + BOOL8 adjust_grey //shift to new bpp + ); + //reduce rectangle +extern DLLSYM void reduce_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //reduction factor + BOOL8 adjust_grey //shift to new bpp + ); +extern DLLSYM void invert_image( /*invert the image */ + IMAGE *image /*image ot invert */ + ); + //bias rectangle +extern DLLSYM void bias_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + UINT8 bias //number to add + ); + //copy rectangle +extern DLLSYM void starbase_to_normal(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + BOOL8 preserve_grey //shift to new bpp + ); +#endif diff --git a/image/imgtiff.cpp b/image/imgtiff.cpp new file mode 100644 index 0000000000..d76dafcf4e --- /dev/null +++ b/image/imgtiff.cpp @@ -0,0 +1,705 @@ +/********************************************************************** + * File: imgtiff.c (Formerly tiff.c) + * Description: Max format image reader/writer. + * Author: Ray Smith + * Created: Mon Jun 11 14:00:21 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#ifdef __MSW32__ +#include +#else +#include +#endif +#include "fileerr.h" +#include "imgerrs.h" +#include "img.h" +#include "bitstrm.h" +#include "tprintf.h" +#include "serialis.h" +#include "imgtiff.h" + +#define INTEL 0x4949 +#define MOTO 0x4d4d + +/************************************************************************* + * NOTE ON BIG-ENDIAN vs LITTLE-ENDIAN + * + * Intel machines store numbers with LSByte in the left position. + * Motorola (and PA_RISC) machines use the opposite byte ordering. + * + * This code is written so that: + * a) it will compile and run on EITHER machine type AND + * b) the program (on either machine) will process tiff file written in either + * Motorola or Intel format. + * + * The code is compiled with a __NATIVE__ define which is either MOTO or INTEL. + * MOTO and INTEL are defined (above) to be the value of the first two bytes of + * a tiff file in either format. (This identifies the filetype). + * + * Subsequent reads and writes normally just reverse the byte order if the + * machine type (__NATIVE__) is not equal to the filetype determined from the + * first two bytes of the tiff file. + * + * A special case is the "value" field of the tag structure. This can contain + * EITHER a 16bit or a 32bit value. According to the "type" field. The 4 cases + * of machine type / file type combinations need to be treated differently in + * the case of 16 bit values + *************************************************************************/ + +#define ENTRIES 19 /*no of entries */ +#define START 8 /*start of tag table */ + +typedef struct +{ + UINT16 tag; //entry tag + UINT16 type; + UINT32 length; + INT32 value; +} TIFFENTRY; //tiff tag entry + +typedef struct myrational +{ + INT32 top; + INT32 bottom; +} MYRATIONAL; //type 5 + +//statics for the run length codes +#define EOL_CODE 0x800 +#define EOL_MASK 0xfff +#define EOL_LENGTH 12 //12 bits +#define SHORT_CODE_SIZE 64 //no of short codes +#define LONG_CODE_SIZE 40 //no of long codes + +static UINT16 short_white_codes[SHORT_CODE_SIZE] = { + 0xac, 0x38, 0xe, 0x1, 0xd, 0x3, 0x7, 0xf, + 0x19, 0x5, 0x1c, 0x2, 0x4, 0x30, 0xb, 0x2b, + 0x15, 0x35, 0x72, 0x18, 0x8, 0x74, 0x60, 0x10, + 0xa, 0x6a, 0x64, 0x12, 0xc, 0x40, 0xc0, 0x58, + 0xd8, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x14, + 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x20, 0xa0, 0x50, + 0xd0, 0x4a, 0xca, 0x2a, 0xaa, 0x24, 0xa4, 0x1a, + 0x9a, 0x5a, 0xda, 0x52, 0xd2, 0x4c, 0xcc, 0x2c +}; +static UINT8 short_white_lengths[SHORT_CODE_SIZE] = { + 8, 6, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 6, 6, 6, 6, + 6, 6, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8 +}; +static UINT16 short_black_codes[SHORT_CODE_SIZE] = { + 0x3b0, 0x2, 0x3, 0x1, 0x6, 0xc, 0x4, 0x18, + 0x28, 0x8, 0x10, 0x50, 0x70, 0x20, 0xe0, 0x30, + 0x3a0, 0x60, 0x40, 0x730, 0xb0, 0x1b0, 0x760, 0xa0, + 0x740, 0xc0, 0x530, 0xd30, + 0x330, 0xb30, 0x160, 0x960, + 0x560, 0xd60, 0x4b0, 0xcb0, + 0x2b0, 0xab0, 0x6b0, 0xeb0, + 0x360, 0xb60, 0x5b0, 0xdb0, + 0x2a0, 0xaa0, 0x6a0, 0xea0, + 0x260, 0xa60, 0x4a0, 0xca0, + 0x240, 0xec0, 0x1c0, 0xe40, + 0x140, 0x1a0, 0x9a0, 0xd40, + 0x340, 0x5a0, 0x660, 0xe60 +}; +static UINT8 short_black_lengths[SHORT_CODE_SIZE] = { + 10, 3, 2, 2, 3, 4, 4, 5, + 6, 6, 7, 7, 7, 8, 8, 9, + 10, 10, 10, 11, 11, 11, 11, 11, + 11, 11, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 +}; +static UINT16 long_white_codes[LONG_CODE_SIZE] = { + 0x1b, 0x9, 0x3a, 0x76, 0x6c, 0xec, 0x26, 0xa6, + 0x16, 0xe6, 0x66, 0x166, 0x96, 0x196, 0x56, 0x156, + 0xd6, 0x1d6, 0x36, 0x136, 0xb6, 0x1b6, 0x32, 0x132, + 0xb2, 0x6, 0x1b2, + 0x80, 0x180, 0x580, 0x480, 0xc80, + 0x280, 0xa80, 0x680, 0xe80, 0x380, 0xb80, 0x780, 0xf80 +}; +static UINT8 long_white_lengths[LONG_CODE_SIZE] = { + 5, 5, 6, 7, 8, 8, 8, 8, + 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 6, 9, 11, 11, 11, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 +}; +static UINT16 long_black_codes[LONG_CODE_SIZE] = { + 0x3c0, 0x130, 0x930, 0xda0, + 0xcc0, 0x2c0, 0xac0, 0x6c0, + 0x16c0, 0xa40, 0x1a40, 0x640, + 0x1640, 0x9c0, 0x19c0, 0x5c0, + 0x15c0, 0xdc0, 0x1dc0, 0x940, + 0x1940, 0x540, 0x1540, 0xb40, + 0x1b40, 0x4c0, 0x14c0, + 0x80, 0x180, 0x580, 0x480, 0xc80, + 0x280, 0xa80, 0x680, 0xe80, 0x380, 0xb80, 0x780, 0xf80 +}; +static UINT8 long_black_lengths[LONG_CODE_SIZE] = { + 10, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 11, 11, 11, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 +}; + +/********************************************************************** + * open_tif_image + * + * Read the header of a tif format image and prepare to read the rest. + **********************************************************************/ + +INT8 open_tif_image( //read header + int fd, //file to read + INT32 *xsize, //size of image + INT32 *ysize, + INT8 *bpp, //bits per pixel + INT8 *photo, //interpretation + INT32 *res //resolution + ) { + INT16 filetype; + INT32 start; //start of tiff directory + INT16 entries; //no of tiff entries + INT32 imagestart; //location of image in file + INT32 resoffset; //location of res + TIFFENTRY tiffentry; //tag table entry + BOOL8 compressed; //compression control + MYRATIONAL resinfo; //resolution + BOOL8 strips = false; //if in strips + + *xsize = -1; //illegal values + *ysize = -1; + *bpp = -1; + *res = -1; + resoffset = -1; + if (read (fd, (char *) &filetype, sizeof filetype) != sizeof filetype + || filetype != INTEL && filetype != MOTO) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Filetype"); + return -1; + } + lseek (fd, 4L, 0); + if (read (fd, (char *) &start, sizeof start) != sizeof start) { + READFAILED.error ("read_tif_image", LOG, "Start of tag table"); + return -1; + } + + if (filetype != __NATIVE__) + start = reverse32 (start); + if (start <= 0) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Start of tag table"); + return -1; + } + lseek (fd, start, 0); + if (read (fd, (char *) &entries, sizeof (INT16)) != sizeof (INT16)) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Size of tag table"); + return -1; + } + if (filetype != __NATIVE__) + entries = reverse16 (entries); + // printf("No of tiff directory entries=%d\n",entries); + imagestart = 0; + compressed = FALSE; + for (; entries-- > 0;) { + if (read (fd, (char *) &tiffentry, sizeof tiffentry) != + sizeof tiffentry) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Tag table entry"); + return -1; + } + if (filetype != __NATIVE__) { + tiffentry.type = reverse16 (tiffentry.type); + tiffentry.tag = reverse16 (tiffentry.tag); + tiffentry.length = reverse32 (tiffentry.length); + } + if (tiffentry.type != 3) { //Full 32bit value + if (filetype != __NATIVE__) + tiffentry.value = reverse32 (tiffentry.value); + } + else { + /* A 16bit value in 4 bytes - handle with care. SEE NOTE at start of file */ + if (__NATIVE__ == MOTO) { + if (filetype == MOTO) //MOTO file on MOTO Machine + tiffentry.value = tiffentry.value >> 16; + else //INTEL file on MOTO Machine + tiffentry.value = reverse32 (tiffentry.value); + } + else { //INTEL Machine + if (filetype == MOTO) //MOTO file on INTEL Machine + tiffentry.value = reverse16 ((UINT16) tiffentry.value); + //INTEL file on INTEL Machine NO ACTION NEEDED + } + //Clear top 2 MSBytes + tiffentry.value &= 0x0000ffff; + } + + // printf("Tag=%x, Type=%x, Length=%x, value=%x\n", + // tiffentry.tag,tiffentry.type,tiffentry.length,tiffentry.value); + switch (tiffentry.tag) { + case 0x101: + *ysize = tiffentry.value; + break; + case 0x100: + *xsize = tiffentry.value; + break; + case 0x102: + if (tiffentry.length == 1) + *bpp = (INT8) tiffentry.value; + else + *bpp = 24; + break; + case 0x111: + imagestart = tiffentry.value; + strips = tiffentry.length > 1; + break; + case 0x103: + if (tiffentry.value == 3) { + compressed = TRUE; + } + else if (tiffentry.value != 1) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Compression"); + return -1; + } + break; + case 0x11a: + case 0x11b: + //resolution + resoffset = tiffentry.value; + break; + case 0x106: + *photo = (INT8) tiffentry.value; + break; + } //endswitch + } + if (*xsize <= 0 || *ysize <= 0 || *bpp > 24 || imagestart <= 0) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Vital tag"); + return -1; + } + tprintf ("Image has %d bit%c per pixel and size (%d,%d)\n", + *bpp, *bpp == 1 ? ' ' : 's', *xsize, *ysize); + if (resoffset >= 0) { + lseek (fd, resoffset, 0); + if (read (fd, (char *) &resinfo, sizeof (resinfo)) != sizeof (resinfo)) { + READFAILED.error ("read_tif_image", LOG, "Resolution"); + return -1; + } + if (filetype != __NATIVE__) { + resinfo.top = reverse32 (resinfo.top); + resinfo.bottom = reverse32 (resinfo.bottom); + } + *res = resinfo.top / resinfo.bottom; + tprintf ("Resolution=%d\n", *res); + } + lseek (fd, (long) imagestart, 0); + if (strips) { + if (read (fd, (char *) &imagestart, sizeof (imagestart)) != + sizeof (imagestart)) { + READFAILED.error ("read_tif_image", LOG, "Strip offset"); + return -1; + } + if (filetype != __NATIVE__) + imagestart = reverse32 (imagestart); + //indirection + lseek (fd, (long) imagestart, 0); + } + return compressed ? -2 : 0; +} + + +/********************************************************************** + * read_tif_image + * + * Read a whole tif image into memory. + **********************************************************************/ + +INT8 read_tif_image( //read whole image + int fd, //file to read + UINT8 *pixels, //pixels of image + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 //bytes per line + ) { + INT32 xindex; //indices in image + INT32 yindex; + INT32 length; //short length + INT32 biglength; //extender + UINT8 *lengths; //current lengths + UINT16 *codes; //current codes + UINT16 codeword; //current code word + IMAGELINE imageline; //current line + IMAGE image; //dummy image + R_BITSTREAM bits; //read bitstream + UINT8 colour; //current colour + + image.capture (pixels, xsize, ysize, bpp); + codeword = bits.open (fd); //open bitstream + read_eol(&bits, codeword); //find end of line + for (yindex = ysize - 1; yindex >= 0; yindex--) { + imageline.init (); + colour = TRUE; + for (xindex = 0; xindex < xsize;) { + if (colour) { + lengths = long_white_lengths; + codes = long_white_codes; + } + else { + lengths = long_black_lengths; + codes = long_black_codes; + } + for (biglength = 0; biglength < LONG_CODE_SIZE + && (codeword & bits.masks (*lengths)) + != *codes; codes++, lengths++, biglength++); + if (biglength < LONG_CODE_SIZE) { + codeword = bits.read_code (*lengths); + biglength++; + biglength *= SHORT_CODE_SIZE; + } + else + biglength = 0; + if (colour) { + lengths = short_white_lengths; + codes = short_white_codes; + } + else { + lengths = short_black_lengths; + codes = short_black_codes; + } + for (length = 0; length < SHORT_CODE_SIZE + && (codeword & bits.masks (*lengths)) + != *codes; codes++, lengths++, length++); + if (length < SHORT_CODE_SIZE) { + codeword = bits.read_code (*lengths); + for (length += biglength; length > 0; length--, xindex++) + imageline.pixels[xindex] = colour; + colour = !colour; + } + else + break; + } + if (xindex < xsize) { + tprintf ("%d pixels short on line %d", xsize - xindex, yindex); + tprintf (", unknown code=%x\n", codeword); + } + xindex = read_eol (&bits, codeword); + if (xindex > 0) + tprintf ("Discarding %d bits on line %d\n", xindex, yindex); + image.put_line (0, yindex, xsize, &imageline, 0); + } + return 0; +} + + +/********************************************************************** + * read_eol + * + * Take bits out of the stream until and end-of-line code is hit. + **********************************************************************/ + +INT32 read_eol( //read end of line + R_BITSTREAM *bits, //bitstream to read + UINT16 &code //current code + ) { + BOOL8 anyones; //any 1 bits skipped + INT32 bitcount; //total bits skipped + + anyones = FALSE; + bitcount = 0; + while ((code & EOL_MASK) != EOL_CODE) { + if (code & 1) + anyones = TRUE; //discarded one bit + bitcount++; //total discarded bits + code = bits->read_code (1); //take single bits + } + //extract EOL code + code = bits->read_code (EOL_LENGTH); + + if (!anyones) + bitcount = 0; //ignore filler bits + return bitcount; +} + + +/********************************************************************** + * write_moto_tif + * + * Write a whole tif format image and close the file. + **********************************************************************/ + +INT8 write_moto_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ) { + return write_tif_image (fd, pixels, xsize, ysize, bpp, res, MOTO, photo); + //use moto format +} + + +/********************************************************************** + * write_intel_tif + * + * Write a whole tif format image and close the file. + **********************************************************************/ + +INT8 write_intel_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ) { + return write_tif_image (fd, pixels, xsize, ysize, bpp, res, INTEL, photo); + //use intel format +} + + +/********************************************************************** + * write_inverse_tif + * + * Write a whole tif format image and close the file. + **********************************************************************/ + +INT8 write_inverse_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ) { + return write_tif_image (fd, pixels, xsize, ysize, bpp, res, INTEL, + 1 - photo); + //use intel format +} + + +/********************************************************************** + * write_tif_image + * + * Write a whole tif format image and close the file. + **********************************************************************/ + +INT8 write_tif_image( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 res, //resolution + INT16 type, //format type + INT16 photo //metric interp + ) { + INT32 size; //line/image size + INT16 entries; //no of tiff entries + INT32 start; //start of tag table + INT32 zero = 0; + MYRATIONAL resolution; //resolution + TIFFENTRY entry; //current entry + + static TIFFENTRY tags[ENTRIES] = { + {0xfe, 4, 1, 0}, + {0x100, 3, 1, 0}, + {0x101, 3, 1, 0}, + {0x102, 3, 1, 0}, + {0x103, 3, 1, 1}, + {0x106, 3, 1, 1}, + { /*line art */ + 0x107, 3, 1, 1 + }, + {0x10a, 3, 1, 1}, + { + 0x111, 4, 1, START + ENTRIES * sizeof (TIFFENTRY) + + sizeof (INT32) + sizeof (short) + sizeof (MYRATIONAL) * 2 + } + , + {0x112, 3, 1, 1} + , + {0x115, 3, 1, 1} + , + {0x116, 4, 1, 0} + , + {0x117, 4, 1, 0} + , + {0x118, 3, 1, 0} + , + {0x119, 3, 1, 1} + , + { + 0x11a, 5, 1, START + ENTRIES * sizeof (TIFFENTRY) + + sizeof (INT32) + sizeof (short) + }, + { + 0x11b, 5, 1, START + ENTRIES * sizeof (TIFFENTRY) + + sizeof (INT32) + sizeof (short) + sizeof (MYRATIONAL) + } + , + {0x11c, 3, 1, 1} + , + {0x128, 3, 1, 2} + }; + + resolution.top = res; + resolution.bottom = 1; + if (write (fd, (char *) &type, sizeof type) != sizeof type + || type != INTEL && type != MOTO) { + WRITEFAILED.error ("write_tif_image", LOG, "Filetype"); + return -1; + } + start = START; + entries = 0x002a; + if (type != __NATIVE__) + entries = reverse16 (entries); + if (write (fd, (char *) &entries, sizeof entries) != sizeof entries) { + WRITEFAILED.error ("write_tif_image", LOG, "Version"); + return -1; + } + if (type != __NATIVE__) + start = reverse32 (start); + if (write (fd, (char *) &start, sizeof start) != sizeof start) { + WRITEFAILED.error ("write_tif_image", LOG, "Start"); + return -1; + } + lseek (fd, (long) START, 0); + entries = ENTRIES; + if (type != __NATIVE__) + entries = reverse16 (entries); + if (write (fd, (char *) &entries, sizeof entries) != sizeof entries) { + WRITEFAILED.error ("write_tif_image", LOG, "Entries"); + return -1; + } + //line length + size = COMPUTE_IMAGE_XDIM (xsize, bpp); + size *= ysize; //total image size + // if (photo==0) + // { + // tags[0].tag=0xfe; + // tags[0].type=4; + // tags[0].value=0; + // } + // else + // { + // tags[0].tag=0xff; + // tags[0].type=3; + // tags[0].value=1; + // } + tags[1].value = xsize; + tags[2].value = ysize; + if (bpp == 24) { + tags[3].value = 8; + tags[10].value = 3; + tags[5].value = 2; + } + else { + tags[3].value = bpp; + tags[5].value = photo; + } + tags[11].value = ysize; + tags[14].value = (1 << bpp) - 1; + tags[12].value = size; + for (entries = 0; entries < ENTRIES; entries++) { + entry = tags[entries]; //get an entry + /* NB Convert entry.value BEFORE converting entry.type!!! */ + if (entry.type != 3) { //Full 32bit value + if (type != __NATIVE__) + entry.value = reverse32 (entry.value); + } + else { + /* A 16bit value in 4 bytes - handle with care. SEE NOTE at start of file */ + entry.value &= 0x0000ffff; //Ensure top 2 MSBytes clear + if (__NATIVE__ == MOTO) { + if (type == MOTO) //MOTO file on MOTO Machine + entry.value = entry.value << 16; + else //INTEL file on MOTO Machine + entry.value = reverse32 (entry.value); + } + else { //INTEL Machine + if (type == MOTO) //MOTO file on INTEL Machine + entry.value = reverse16 ((UINT16) entry.value); + //INTEL file on INTEL Machine NO ACTION NEEDED + } + } + if (type != __NATIVE__) { + entry.tag = reverse16 (entry.tag); + entry.type = reverse16 (entry.type); + entry.length = reverse32 (entry.length); + } + if (write (fd, (char *) &entry, sizeof (TIFFENTRY)) != + sizeof (TIFFENTRY)) { + WRITEFAILED.error ("write_tif_image", LOG, "Tag Table"); + return -1; + } + } + if (write (fd, (char *) &zero, sizeof zero) != sizeof zero) { + WRITEFAILED.error ("write_tif_image", LOG, "Tag table Terminator"); + return -1; + } + if (type != __NATIVE__) { + resolution.top = reverse32 (resolution.top); + resolution.bottom = reverse32 (resolution.bottom); + } + if (write (fd, (char *) &resolution, sizeof resolution) != sizeof resolution + || write (fd, (char *) &resolution, + sizeof resolution) != sizeof resolution) { + WRITEFAILED.error ("write_tif_image", LOG, "Resolution"); + return -1; + } + if (write (fd, (char *) pixels, (size_t) size) != size) { + WRITEFAILED.error ("write_tif_image", LOG, "Image"); + return -1; + } + close(fd); + return 0; +} + + +/********************************************************************** + * reverse32 + * + * Byte swap the 32 bit number between Motorola & Intel format. + **********************************************************************/ + +//INT32 reverse32( //reverse 32 bit int +//UINT32 value //value to reverse +//) +//{ +// return (value>>24) | (value>>8) & 0xff00 +// | (value<<8) & 0xff0000 | (value<<24); +//} + +/********************************************************************** + * reverse16 + * + * Byte swap the 16 bit number between Motorola & Intel format. + **********************************************************************/ + +//INT16 reverse16( //reverse 16 bit int +//UINT16 value //value to reverse +//) +//{ +// return (value>>8) | (value<<8); +//} diff --git a/image/imgtiff.h b/image/imgtiff.h new file mode 100644 index 0000000000..58fc077507 --- /dev/null +++ b/image/imgtiff.h @@ -0,0 +1,89 @@ +/********************************************************************** + * File: imgtiff.h (Formerly tiff.h) + * Description: Header file for tiff format image reader/writer. + * Author: Ray Smith + * Created: Mon Jun 11 15:19:41 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef IMGTIFF_H +#define IMGTIFF_H + +#include "host.h" +#include "bitstrm.h" + +INT8 open_tif_image( //read header + int fd, //file to read + INT32 *xsize, //size of image + INT32 *ysize, + INT8 *bpp, //bits per pixel + INT8 *photo, //interpretation + INT32 *res //resolution + ); +INT8 read_tif_image( //read whole image + int fd, //file to read + UINT8 *pixels, //pixels of image + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 //bytes per line + ); +INT32 read_eol( //read end of line + R_BITSTREAM *bits, //bitstream to read + UINT16 &code //current code + ); +INT8 write_moto_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ); +INT8 write_intel_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ); +INT8 write_inverse_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ); +INT8 write_tif_image( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 res, //resolution + INT16 type, //format type + INT16 photo //metric interp + ); +//INT32 reverse32( //reverse 32 bit int +//UINT32 value //value to reverse +//); +//INT16 reverse16( //reverse 16 bit int +//UINT16 value //value to reverse +//); +#endif diff --git a/image/imgunpk.h b/image/imgunpk.h new file mode 100644 index 0000000000..d7a28e0ea1 --- /dev/null +++ b/image/imgunpk.h @@ -0,0 +1,1377 @@ +/********************************************************************** + * File: imgunpk.h (Formerly unpack.h) + * Description: Definitions of fast unpacking look-up tables for images.c. + * Author: Ray Smith + * Created: Mon Jun 11 18:06:34 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef IMGUNPK_H +#define IMGUNPK_H + +#include "host.h" + +UINT8 bpp4table[256][2] = { /*4 bit converter */ + {0, 0} + , {0, 1} + , {0, 2} + , {0, 3} + , + {0, 4} + , {0, 5} + , {0, 6} + , {0, 7} + , + {0, 8} + , {0, 9} + , {0, 10} + , {0, 11} + , + {0, 12} + , {0, 13} + , {0, 14} + , {0, 15} + , + {1, 0} + , {1, 1} + , {1, 2} + , {1, 3} + , + {1, 4} + , {1, 5} + , {1, 6} + , {1, 7} + , + {1, 8} + , {1, 9} + , {1, 10} + , {1, 11} + , + {1, 12} + , {1, 13} + , {1, 14} + , {1, 15} + , + {2, 0} + , {2, 1} + , {2, 2} + , {2, 3} + , + {2, 4} + , {2, 5} + , {2, 6} + , {2, 7} + , + {2, 8} + , {2, 9} + , {2, 10} + , {2, 11} + , + {2, 12} + , {2, 13} + , {2, 14} + , {2, 15} + , + {3, 0} + , {3, 1} + , {3, 2} + , {3, 3} + , + {3, 4} + , {3, 5} + , {3, 6} + , {3, 7} + , + {3, 8} + , {3, 9} + , {3, 10} + , {3, 11} + , + {3, 12} + , {3, 13} + , {3, 14} + , {3, 15} + , + {4, 0} + , {4, 1} + , {4, 2} + , {4, 3} + , + {4, 4} + , {4, 5} + , {4, 6} + , {4, 7} + , + {4, 8} + , {4, 9} + , {4, 10} + , {4, 11} + , + {4, 12} + , {4, 13} + , {4, 14} + , {4, 15} + , + {5, 0} + , {5, 1} + , {5, 2} + , {5, 3} + , + {5, 4} + , {5, 5} + , {5, 6} + , {5, 7} + , + {5, 8} + , {5, 9} + , {5, 10} + , {5, 11} + , + {5, 12} + , {5, 13} + , {5, 14} + , {5, 15} + , + {6, 0} + , {6, 1} + , {6, 2} + , {6, 3} + , + {6, 4} + , {6, 5} + , {6, 6} + , {6, 7} + , + {6, 8} + , {6, 9} + , {6, 10} + , {6, 11} + , + {6, 12} + , {6, 13} + , {6, 14} + , {6, 15} + , + {7, 0} + , {7, 1} + , {7, 2} + , {7, 3} + , + {7, 4} + , {7, 5} + , {7, 6} + , {7, 7} + , + {7, 8} + , {7, 9} + , {7, 10} + , {7, 11} + , + {7, 12} + , {7, 13} + , {7, 14} + , {7, 15} + , + {8, 0} + , {8, 1} + , {8, 2} + , {8, 3} + , + {8, 4} + , {8, 5} + , {8, 6} + , {8, 7} + , + {8, 8} + , {8, 9} + , {8, 10} + , {8, 11} + , + {8, 12} + , {8, 13} + , {8, 14} + , {8, 15} + , + {9, 0} + , {9, 1} + , {9, 2} + , {9, 3} + , + {9, 4} + , {9, 5} + , {9, 6} + , {9, 7} + , + {9, 8} + , {9, 9} + , {9, 10} + , {9, 11} + , + {9, 12} + , {9, 13} + , {9, 14} + , {9, 15} + , + {10, 0} + , {10, 1} + , {10, 2} + , {10, 3} + , + {10, 4} + , {10, 5} + , {10, 6} + , {10, 7} + , + {10, 8} + , {10, 9} + , {10, 10} + , {10, 11} + , + {10, 12} + , {10, 13} + , {10, 14} + , {10, 15} + , + {11, 0} + , {11, 1} + , {11, 2} + , {11, 3} + , + {11, 4} + , {11, 5} + , {11, 6} + , {11, 7} + , + {11, 8} + , {11, 9} + , {11, 10} + , {11, 11} + , + {11, 12} + , {11, 13} + , {11, 14} + , {11, 15} + , + {12, 0} + , {12, 1} + , {12, 2} + , {12, 3} + , + {12, 4} + , {12, 5} + , {12, 6} + , {12, 7} + , + {12, 8} + , {12, 9} + , {12, 10} + , {12, 11} + , + {12, 12} + , {12, 13} + , {12, 14} + , {12, 15} + , + {13, 0} + , {13, 1} + , {13, 2} + , {13, 3} + , + {13, 4} + , {13, 5} + , {13, 6} + , {13, 7} + , + {13, 8} + , {13, 9} + , {13, 10} + , {13, 11} + , + {13, 12} + , {13, 13} + , {13, 14} + , {13, 15} + , + {14, 0} + , {14, 1} + , {14, 2} + , {14, 3} + , + {14, 4} + , {14, 5} + , {14, 6} + , {14, 7} + , + {14, 8} + , {14, 9} + , {14, 10} + , {14, 11} + , + {14, 12} + , {14, 13} + , {14, 14} + , {14, 15} + , + {15, 0} + , {15, 1} + , {15, 2} + , {15, 3} + , + {15, 4} + , {15, 5} + , {15, 6} + , {15, 7} + , + {15, 8} + , {15, 9} + , {15, 10} + , {15, 11} + , + {15, 12} + , {15, 13} + , {15, 14} + , {15, 15} + , +}; + +UINT8 bpp2table[256][4] = { /*2bpp converter */ + {0, 0, 0, 0} + , + {0, 0, 0, 1} + , + {0, 0, 0, 2} + , + {0, 0, 0, 3} + , + {0, 0, 1, 0} + , + {0, 0, 1, 1} + , + {0, 0, 1, 2} + , + {0, 0, 1, 3} + , + {0, 0, 2, 0} + , + {0, 0, 2, 1} + , + {0, 0, 2, 2} + , + {0, 0, 2, 3} + , + {0, 0, 3, 0} + , + {0, 0, 3, 1} + , + {0, 0, 3, 2} + , + {0, 0, 3, 3} + , + {0, 1, 0, 0} + , + {0, 1, 0, 1} + , + {0, 1, 0, 2} + , + {0, 1, 0, 3} + , + {0, 1, 1, 0} + , + {0, 1, 1, 1} + , + {0, 1, 1, 2} + , + {0, 1, 1, 3} + , + {0, 1, 2, 0} + , + {0, 1, 2, 1} + , + {0, 1, 2, 2} + , + {0, 1, 2, 3} + , + {0, 1, 3, 0} + , + {0, 1, 3, 1} + , + {0, 1, 3, 2} + , + {0, 1, 3, 3} + , + {0, 2, 0, 0} + , + {0, 2, 0, 1} + , + {0, 2, 0, 2} + , + {0, 2, 0, 3} + , + {0, 2, 1, 0} + , + {0, 2, 1, 1} + , + {0, 2, 1, 2} + , + {0, 2, 1, 3} + , + {0, 2, 2, 0} + , + {0, 2, 2, 1} + , + {0, 2, 2, 2} + , + {0, 2, 2, 3} + , + {0, 2, 3, 0} + , + {0, 2, 3, 1} + , + {0, 2, 3, 2} + , + {0, 2, 3, 3} + , + {0, 3, 0, 0} + , + {0, 3, 0, 1} + , + {0, 3, 0, 2} + , + {0, 3, 0, 3} + , + {0, 3, 1, 0} + , + {0, 3, 1, 1} + , + {0, 3, 1, 2} + , + {0, 3, 1, 3} + , + {0, 3, 2, 0} + , + {0, 3, 2, 1} + , + {0, 3, 2, 2} + , + {0, 3, 2, 3} + , + {0, 3, 3, 0} + , + {0, 3, 3, 1} + , + {0, 3, 3, 2} + , + {0, 3, 3, 3} + , + {1, 0, 0, 0} + , + {1, 0, 0, 1} + , + {1, 0, 0, 2} + , + {1, 0, 0, 3} + , + {1, 0, 1, 0} + , + {1, 0, 1, 1} + , + {1, 0, 1, 2} + , + {1, 0, 1, 3} + , + {1, 0, 2, 0} + , + {1, 0, 2, 1} + , + {1, 0, 2, 2} + , + {1, 0, 2, 3} + , + {1, 0, 3, 0} + , + {1, 0, 3, 1} + , + {1, 0, 3, 2} + , + {1, 0, 3, 3} + , + {1, 1, 0, 0} + , + {1, 1, 0, 1} + , + {1, 1, 0, 2} + , + {1, 1, 0, 3} + , + {1, 1, 1, 0} + , + {1, 1, 1, 1} + , + {1, 1, 1, 2} + , + {1, 1, 1, 3} + , + {1, 1, 2, 0} + , + {1, 1, 2, 1} + , + {1, 1, 2, 2} + , + {1, 1, 2, 3} + , + {1, 1, 3, 0} + , + {1, 1, 3, 1} + , + {1, 1, 3, 2} + , + {1, 1, 3, 3} + , + {1, 2, 0, 0} + , + {1, 2, 0, 1} + , + {1, 2, 0, 2} + , + {1, 2, 0, 3} + , + {1, 2, 1, 0} + , + {1, 2, 1, 1} + , + {1, 2, 1, 2} + , + {1, 2, 1, 3} + , + {1, 2, 2, 0} + , + {1, 2, 2, 1} + , + {1, 2, 2, 2} + , + {1, 2, 2, 3} + , + {1, 2, 3, 0} + , + {1, 2, 3, 1} + , + {1, 2, 3, 2} + , + {1, 2, 3, 3} + , + {1, 3, 0, 0} + , + {1, 3, 0, 1} + , + {1, 3, 0, 2} + , + {1, 3, 0, 3} + , + {1, 3, 1, 0} + , + {1, 3, 1, 1} + , + {1, 3, 1, 2} + , + {1, 3, 1, 3} + , + {1, 3, 2, 0} + , + {1, 3, 2, 1} + , + {1, 3, 2, 2} + , + {1, 3, 2, 3} + , + {1, 3, 3, 0} + , + {1, 3, 3, 1} + , + {1, 3, 3, 2} + , + {1, 3, 3, 3} + , + {2, 0, 0, 0} + , + {2, 0, 0, 1} + , + {2, 0, 0, 2} + , + {2, 0, 0, 3} + , + {2, 0, 1, 0} + , + {2, 0, 1, 1} + , + {2, 0, 1, 2} + , + {2, 0, 1, 3} + , + {2, 0, 2, 0} + , + {2, 0, 2, 1} + , + {2, 0, 2, 2} + , + {2, 0, 2, 3} + , + {2, 0, 3, 0} + , + {2, 0, 3, 1} + , + {2, 0, 3, 2} + , + {2, 0, 3, 3} + , + {2, 1, 0, 0} + , + {2, 1, 0, 1} + , + {2, 1, 0, 2} + , + {2, 1, 0, 3} + , + {2, 1, 1, 0} + , + {2, 1, 1, 1} + , + {2, 1, 1, 2} + , + {2, 1, 1, 3} + , + {2, 1, 2, 0} + , + {2, 1, 2, 1} + , + {2, 1, 2, 2} + , + {2, 1, 2, 3} + , + {2, 1, 3, 0} + , + {2, 1, 3, 1} + , + {2, 1, 3, 2} + , + {2, 1, 3, 3} + , + {2, 2, 0, 0} + , + {2, 2, 0, 1} + , + {2, 2, 0, 2} + , + {2, 2, 0, 3} + , + {2, 2, 1, 0} + , + {2, 2, 1, 1} + , + {2, 2, 1, 2} + , + {2, 2, 1, 3} + , + {2, 2, 2, 0} + , + {2, 2, 2, 1} + , + {2, 2, 2, 2} + , + {2, 2, 2, 3} + , + {2, 2, 3, 0} + , + {2, 2, 3, 1} + , + {2, 2, 3, 2} + , + {2, 2, 3, 3} + , + {2, 3, 0, 0} + , + {2, 3, 0, 1} + , + {2, 3, 0, 2} + , + {2, 3, 0, 3} + , + {2, 3, 1, 0} + , + {2, 3, 1, 1} + , + {2, 3, 1, 2} + , + {2, 3, 1, 3} + , + {2, 3, 2, 0} + , + {2, 3, 2, 1} + , + {2, 3, 2, 2} + , + {2, 3, 2, 3} + , + {2, 3, 3, 0} + , + {2, 3, 3, 1} + , + {2, 3, 3, 2} + , + {2, 3, 3, 3} + , + {3, 0, 0, 0} + , + {3, 0, 0, 1} + , + {3, 0, 0, 2} + , + {3, 0, 0, 3} + , + {3, 0, 1, 0} + , + {3, 0, 1, 1} + , + {3, 0, 1, 2} + , + {3, 0, 1, 3} + , + {3, 0, 2, 0} + , + {3, 0, 2, 1} + , + {3, 0, 2, 2} + , + {3, 0, 2, 3} + , + {3, 0, 3, 0} + , + {3, 0, 3, 1} + , + {3, 0, 3, 2} + , + {3, 0, 3, 3} + , + {3, 1, 0, 0} + , + {3, 1, 0, 1} + , + {3, 1, 0, 2} + , + {3, 1, 0, 3} + , + {3, 1, 1, 0} + , + {3, 1, 1, 1} + , + {3, 1, 1, 2} + , + {3, 1, 1, 3} + , + {3, 1, 2, 0} + , + {3, 1, 2, 1} + , + {3, 1, 2, 2} + , + {3, 1, 2, 3} + , + {3, 1, 3, 0} + , + {3, 1, 3, 1} + , + {3, 1, 3, 2} + , + {3, 1, 3, 3} + , + {3, 2, 0, 0} + , + {3, 2, 0, 1} + , + {3, 2, 0, 2} + , + {3, 2, 0, 3} + , + {3, 2, 1, 0} + , + {3, 2, 1, 1} + , + {3, 2, 1, 2} + , + {3, 2, 1, 3} + , + {3, 2, 2, 0} + , + {3, 2, 2, 1} + , + {3, 2, 2, 2} + , + {3, 2, 2, 3} + , + {3, 2, 3, 0} + , + {3, 2, 3, 1} + , + {3, 2, 3, 2} + , + {3, 2, 3, 3} + , + {3, 3, 0, 0} + , + {3, 3, 0, 1} + , + {3, 3, 0, 2} + , + {3, 3, 0, 3} + , + {3, 3, 1, 0} + , + {3, 3, 1, 1} + , + {3, 3, 1, 2} + , + {3, 3, 1, 3} + , + {3, 3, 2, 0} + , + {3, 3, 2, 1} + , + {3, 3, 2, 2} + , + {3, 3, 2, 3} + , + {3, 3, 3, 0} + , + {3, 3, 3, 1} + , + {3, 3, 3, 2} + , + {3, 3, 3, 3} + , +}; + +UINT8 bpp1table[256][8] = { /*1bpp converter */ + {0, 0, 0, 0, 0, 0, 0, 0} + , + {0, 0, 0, 0, 0, 0, 0, 1} + , + {0, 0, 0, 0, 0, 0, 1, 0} + , + {0, 0, 0, 0, 0, 0, 1, 1} + , + {0, 0, 0, 0, 0, 1, 0, 0} + , + {0, 0, 0, 0, 0, 1, 0, 1} + , + {0, 0, 0, 0, 0, 1, 1, 0} + , + {0, 0, 0, 0, 0, 1, 1, 1} + , + {0, 0, 0, 0, 1, 0, 0, 0} + , + {0, 0, 0, 0, 1, 0, 0, 1} + , + {0, 0, 0, 0, 1, 0, 1, 0} + , + {0, 0, 0, 0, 1, 0, 1, 1} + , + {0, 0, 0, 0, 1, 1, 0, 0} + , + {0, 0, 0, 0, 1, 1, 0, 1} + , + {0, 0, 0, 0, 1, 1, 1, 0} + , + {0, 0, 0, 0, 1, 1, 1, 1} + , + {0, 0, 0, 1, 0, 0, 0, 0} + , + {0, 0, 0, 1, 0, 0, 0, 1} + , + {0, 0, 0, 1, 0, 0, 1, 0} + , + {0, 0, 0, 1, 0, 0, 1, 1} + , + {0, 0, 0, 1, 0, 1, 0, 0} + , + {0, 0, 0, 1, 0, 1, 0, 1} + , + {0, 0, 0, 1, 0, 1, 1, 0} + , + {0, 0, 0, 1, 0, 1, 1, 1} + , + {0, 0, 0, 1, 1, 0, 0, 0} + , + {0, 0, 0, 1, 1, 0, 0, 1} + , + {0, 0, 0, 1, 1, 0, 1, 0} + , + {0, 0, 0, 1, 1, 0, 1, 1} + , + {0, 0, 0, 1, 1, 1, 0, 0} + , + {0, 0, 0, 1, 1, 1, 0, 1} + , + {0, 0, 0, 1, 1, 1, 1, 0} + , + {0, 0, 0, 1, 1, 1, 1, 1} + , + {0, 0, 1, 0, 0, 0, 0, 0} + , + {0, 0, 1, 0, 0, 0, 0, 1} + , + {0, 0, 1, 0, 0, 0, 1, 0} + , + {0, 0, 1, 0, 0, 0, 1, 1} + , + {0, 0, 1, 0, 0, 1, 0, 0} + , + {0, 0, 1, 0, 0, 1, 0, 1} + , + {0, 0, 1, 0, 0, 1, 1, 0} + , + {0, 0, 1, 0, 0, 1, 1, 1} + , + {0, 0, 1, 0, 1, 0, 0, 0} + , + {0, 0, 1, 0, 1, 0, 0, 1} + , + {0, 0, 1, 0, 1, 0, 1, 0} + , + {0, 0, 1, 0, 1, 0, 1, 1} + , + {0, 0, 1, 0, 1, 1, 0, 0} + , + {0, 0, 1, 0, 1, 1, 0, 1} + , + {0, 0, 1, 0, 1, 1, 1, 0} + , + {0, 0, 1, 0, 1, 1, 1, 1} + , + {0, 0, 1, 1, 0, 0, 0, 0} + , + {0, 0, 1, 1, 0, 0, 0, 1} + , + {0, 0, 1, 1, 0, 0, 1, 0} + , + {0, 0, 1, 1, 0, 0, 1, 1} + , + {0, 0, 1, 1, 0, 1, 0, 0} + , + {0, 0, 1, 1, 0, 1, 0, 1} + , + {0, 0, 1, 1, 0, 1, 1, 0} + , + {0, 0, 1, 1, 0, 1, 1, 1} + , + {0, 0, 1, 1, 1, 0, 0, 0} + , + {0, 0, 1, 1, 1, 0, 0, 1} + , + {0, 0, 1, 1, 1, 0, 1, 0} + , + {0, 0, 1, 1, 1, 0, 1, 1} + , + {0, 0, 1, 1, 1, 1, 0, 0} + , + {0, 0, 1, 1, 1, 1, 0, 1} + , + {0, 0, 1, 1, 1, 1, 1, 0} + , + {0, 0, 1, 1, 1, 1, 1, 1} + , + {0, 1, 0, 0, 0, 0, 0, 0} + , + {0, 1, 0, 0, 0, 0, 0, 1} + , + {0, 1, 0, 0, 0, 0, 1, 0} + , + {0, 1, 0, 0, 0, 0, 1, 1} + , + {0, 1, 0, 0, 0, 1, 0, 0} + , + {0, 1, 0, 0, 0, 1, 0, 1} + , + {0, 1, 0, 0, 0, 1, 1, 0} + , + {0, 1, 0, 0, 0, 1, 1, 1} + , + {0, 1, 0, 0, 1, 0, 0, 0} + , + {0, 1, 0, 0, 1, 0, 0, 1} + , + {0, 1, 0, 0, 1, 0, 1, 0} + , + {0, 1, 0, 0, 1, 0, 1, 1} + , + {0, 1, 0, 0, 1, 1, 0, 0} + , + {0, 1, 0, 0, 1, 1, 0, 1} + , + {0, 1, 0, 0, 1, 1, 1, 0} + , + {0, 1, 0, 0, 1, 1, 1, 1} + , + {0, 1, 0, 1, 0, 0, 0, 0} + , + {0, 1, 0, 1, 0, 0, 0, 1} + , + {0, 1, 0, 1, 0, 0, 1, 0} + , + {0, 1, 0, 1, 0, 0, 1, 1} + , + {0, 1, 0, 1, 0, 1, 0, 0} + , + {0, 1, 0, 1, 0, 1, 0, 1} + , + {0, 1, 0, 1, 0, 1, 1, 0} + , + {0, 1, 0, 1, 0, 1, 1, 1} + , + {0, 1, 0, 1, 1, 0, 0, 0} + , + {0, 1, 0, 1, 1, 0, 0, 1} + , + {0, 1, 0, 1, 1, 0, 1, 0} + , + {0, 1, 0, 1, 1, 0, 1, 1} + , + {0, 1, 0, 1, 1, 1, 0, 0} + , + {0, 1, 0, 1, 1, 1, 0, 1} + , + {0, 1, 0, 1, 1, 1, 1, 0} + , + {0, 1, 0, 1, 1, 1, 1, 1} + , + {0, 1, 1, 0, 0, 0, 0, 0} + , + {0, 1, 1, 0, 0, 0, 0, 1} + , + {0, 1, 1, 0, 0, 0, 1, 0} + , + {0, 1, 1, 0, 0, 0, 1, 1} + , + {0, 1, 1, 0, 0, 1, 0, 0} + , + {0, 1, 1, 0, 0, 1, 0, 1} + , + {0, 1, 1, 0, 0, 1, 1, 0} + , + {0, 1, 1, 0, 0, 1, 1, 1} + , + {0, 1, 1, 0, 1, 0, 0, 0} + , + {0, 1, 1, 0, 1, 0, 0, 1} + , + {0, 1, 1, 0, 1, 0, 1, 0} + , + {0, 1, 1, 0, 1, 0, 1, 1} + , + {0, 1, 1, 0, 1, 1, 0, 0} + , + {0, 1, 1, 0, 1, 1, 0, 1} + , + {0, 1, 1, 0, 1, 1, 1, 0} + , + {0, 1, 1, 0, 1, 1, 1, 1} + , + {0, 1, 1, 1, 0, 0, 0, 0} + , + {0, 1, 1, 1, 0, 0, 0, 1} + , + {0, 1, 1, 1, 0, 0, 1, 0} + , + {0, 1, 1, 1, 0, 0, 1, 1} + , + {0, 1, 1, 1, 0, 1, 0, 0} + , + {0, 1, 1, 1, 0, 1, 0, 1} + , + {0, 1, 1, 1, 0, 1, 1, 0} + , + {0, 1, 1, 1, 0, 1, 1, 1} + , + {0, 1, 1, 1, 1, 0, 0, 0} + , + {0, 1, 1, 1, 1, 0, 0, 1} + , + {0, 1, 1, 1, 1, 0, 1, 0} + , + {0, 1, 1, 1, 1, 0, 1, 1} + , + {0, 1, 1, 1, 1, 1, 0, 0} + , + {0, 1, 1, 1, 1, 1, 0, 1} + , + {0, 1, 1, 1, 1, 1, 1, 0} + , + {0, 1, 1, 1, 1, 1, 1, 1} + , + {1, 0, 0, 0, 0, 0, 0, 0} + , + {1, 0, 0, 0, 0, 0, 0, 1} + , + {1, 0, 0, 0, 0, 0, 1, 0} + , + {1, 0, 0, 0, 0, 0, 1, 1} + , + {1, 0, 0, 0, 0, 1, 0, 0} + , + {1, 0, 0, 0, 0, 1, 0, 1} + , + {1, 0, 0, 0, 0, 1, 1, 0} + , + {1, 0, 0, 0, 0, 1, 1, 1} + , + {1, 0, 0, 0, 1, 0, 0, 0} + , + {1, 0, 0, 0, 1, 0, 0, 1} + , + {1, 0, 0, 0, 1, 0, 1, 0} + , + {1, 0, 0, 0, 1, 0, 1, 1} + , + {1, 0, 0, 0, 1, 1, 0, 0} + , + {1, 0, 0, 0, 1, 1, 0, 1} + , + {1, 0, 0, 0, 1, 1, 1, 0} + , + {1, 0, 0, 0, 1, 1, 1, 1} + , + {1, 0, 0, 1, 0, 0, 0, 0} + , + {1, 0, 0, 1, 0, 0, 0, 1} + , + {1, 0, 0, 1, 0, 0, 1, 0} + , + {1, 0, 0, 1, 0, 0, 1, 1} + , + {1, 0, 0, 1, 0, 1, 0, 0} + , + {1, 0, 0, 1, 0, 1, 0, 1} + , + {1, 0, 0, 1, 0, 1, 1, 0} + , + {1, 0, 0, 1, 0, 1, 1, 1} + , + {1, 0, 0, 1, 1, 0, 0, 0} + , + {1, 0, 0, 1, 1, 0, 0, 1} + , + {1, 0, 0, 1, 1, 0, 1, 0} + , + {1, 0, 0, 1, 1, 0, 1, 1} + , + {1, 0, 0, 1, 1, 1, 0, 0} + , + {1, 0, 0, 1, 1, 1, 0, 1} + , + {1, 0, 0, 1, 1, 1, 1, 0} + , + {1, 0, 0, 1, 1, 1, 1, 1} + , + {1, 0, 1, 0, 0, 0, 0, 0} + , + {1, 0, 1, 0, 0, 0, 0, 1} + , + {1, 0, 1, 0, 0, 0, 1, 0} + , + {1, 0, 1, 0, 0, 0, 1, 1} + , + {1, 0, 1, 0, 0, 1, 0, 0} + , + {1, 0, 1, 0, 0, 1, 0, 1} + , + {1, 0, 1, 0, 0, 1, 1, 0} + , + {1, 0, 1, 0, 0, 1, 1, 1} + , + {1, 0, 1, 0, 1, 0, 0, 0} + , + {1, 0, 1, 0, 1, 0, 0, 1} + , + {1, 0, 1, 0, 1, 0, 1, 0} + , + {1, 0, 1, 0, 1, 0, 1, 1} + , + {1, 0, 1, 0, 1, 1, 0, 0} + , + {1, 0, 1, 0, 1, 1, 0, 1} + , + {1, 0, 1, 0, 1, 1, 1, 0} + , + {1, 0, 1, 0, 1, 1, 1, 1} + , + {1, 0, 1, 1, 0, 0, 0, 0} + , + {1, 0, 1, 1, 0, 0, 0, 1} + , + {1, 0, 1, 1, 0, 0, 1, 0} + , + {1, 0, 1, 1, 0, 0, 1, 1} + , + {1, 0, 1, 1, 0, 1, 0, 0} + , + {1, 0, 1, 1, 0, 1, 0, 1} + , + {1, 0, 1, 1, 0, 1, 1, 0} + , + {1, 0, 1, 1, 0, 1, 1, 1} + , + {1, 0, 1, 1, 1, 0, 0, 0} + , + {1, 0, 1, 1, 1, 0, 0, 1} + , + {1, 0, 1, 1, 1, 0, 1, 0} + , + {1, 0, 1, 1, 1, 0, 1, 1} + , + {1, 0, 1, 1, 1, 1, 0, 0} + , + {1, 0, 1, 1, 1, 1, 0, 1} + , + {1, 0, 1, 1, 1, 1, 1, 0} + , + {1, 0, 1, 1, 1, 1, 1, 1} + , + {1, 1, 0, 0, 0, 0, 0, 0} + , + {1, 1, 0, 0, 0, 0, 0, 1} + , + {1, 1, 0, 0, 0, 0, 1, 0} + , + {1, 1, 0, 0, 0, 0, 1, 1} + , + {1, 1, 0, 0, 0, 1, 0, 0} + , + {1, 1, 0, 0, 0, 1, 0, 1} + , + {1, 1, 0, 0, 0, 1, 1, 0} + , + {1, 1, 0, 0, 0, 1, 1, 1} + , + {1, 1, 0, 0, 1, 0, 0, 0} + , + {1, 1, 0, 0, 1, 0, 0, 1} + , + {1, 1, 0, 0, 1, 0, 1, 0} + , + {1, 1, 0, 0, 1, 0, 1, 1} + , + {1, 1, 0, 0, 1, 1, 0, 0} + , + {1, 1, 0, 0, 1, 1, 0, 1} + , + {1, 1, 0, 0, 1, 1, 1, 0} + , + {1, 1, 0, 0, 1, 1, 1, 1} + , + {1, 1, 0, 1, 0, 0, 0, 0} + , + {1, 1, 0, 1, 0, 0, 0, 1} + , + {1, 1, 0, 1, 0, 0, 1, 0} + , + {1, 1, 0, 1, 0, 0, 1, 1} + , + {1, 1, 0, 1, 0, 1, 0, 0} + , + {1, 1, 0, 1, 0, 1, 0, 1} + , + {1, 1, 0, 1, 0, 1, 1, 0} + , + {1, 1, 0, 1, 0, 1, 1, 1} + , + {1, 1, 0, 1, 1, 0, 0, 0} + , + {1, 1, 0, 1, 1, 0, 0, 1} + , + {1, 1, 0, 1, 1, 0, 1, 0} + , + {1, 1, 0, 1, 1, 0, 1, 1} + , + {1, 1, 0, 1, 1, 1, 0, 0} + , + {1, 1, 0, 1, 1, 1, 0, 1} + , + {1, 1, 0, 1, 1, 1, 1, 0} + , + {1, 1, 0, 1, 1, 1, 1, 1} + , + {1, 1, 1, 0, 0, 0, 0, 0} + , + {1, 1, 1, 0, 0, 0, 0, 1} + , + {1, 1, 1, 0, 0, 0, 1, 0} + , + {1, 1, 1, 0, 0, 0, 1, 1} + , + {1, 1, 1, 0, 0, 1, 0, 0} + , + {1, 1, 1, 0, 0, 1, 0, 1} + , + {1, 1, 1, 0, 0, 1, 1, 0} + , + {1, 1, 1, 0, 0, 1, 1, 1} + , + {1, 1, 1, 0, 1, 0, 0, 0} + , + {1, 1, 1, 0, 1, 0, 0, 1} + , + {1, 1, 1, 0, 1, 0, 1, 0} + , + {1, 1, 1, 0, 1, 0, 1, 1} + , + {1, 1, 1, 0, 1, 1, 0, 0} + , + {1, 1, 1, 0, 1, 1, 0, 1} + , + {1, 1, 1, 0, 1, 1, 1, 0} + , + {1, 1, 1, 0, 1, 1, 1, 1} + , + {1, 1, 1, 1, 0, 0, 0, 0} + , + {1, 1, 1, 1, 0, 0, 0, 1} + , + {1, 1, 1, 1, 0, 0, 1, 0} + , + {1, 1, 1, 1, 0, 0, 1, 1} + , + {1, 1, 1, 1, 0, 1, 0, 0} + , + {1, 1, 1, 1, 0, 1, 0, 1} + , + {1, 1, 1, 1, 0, 1, 1, 0} + , + {1, 1, 1, 1, 0, 1, 1, 1} + , + {1, 1, 1, 1, 1, 0, 0, 0} + , + {1, 1, 1, 1, 1, 0, 0, 1} + , + {1, 1, 1, 1, 1, 0, 1, 0} + , + {1, 1, 1, 1, 1, 0, 1, 1} + , + {1, 1, 1, 1, 1, 1, 0, 0} + , + {1, 1, 1, 1, 1, 1, 0, 1} + , + {1, 1, 1, 1, 1, 1, 1, 0} + , + {1, 1, 1, 1, 1, 1, 1, 1} + , +}; +#endif diff --git a/phototest.tif b/phototest.tif new file mode 100644 index 0000000000..adac046dba Binary files /dev/null and b/phototest.tif differ diff --git a/tessdata/DangAmbigs b/tessdata/DangAmbigs new file mode 100755 index 0000000000..c0d766d0c2 --- /dev/null +++ b/tessdata/DangAmbigs @@ -0,0 +1,39 @@ +m rn +rn m +m in +in m +d cl +cl d +nn rm +rm nn +n ri +ri n +li h +lr h +ii u +ii n +ni m +iii m +ll H +I-I H +vv w +VV W +t f +f t +a o +o a +e c +c e +rr n +E fi +l< k +ld ki +lx h +xn m +ux in +r t +d tl +di th +ur in +un im +u a diff --git a/tessdata/blackText.params b/tessdata/blackText.params new file mode 100755 index 0000000000..777f40dac9 --- /dev/null +++ b/tessdata/blackText.params @@ -0,0 +1,74 @@ +# parameters for black text enhancement + + +zeroThres 5 # 1D histogram (R, G or B) zero threshold. + # Used to identify those parts of the histogram that + # are away from peaks and have returned to zero. + # Per intensity resolution unit and hence independent + # of the number of histogram bins. + # Default 5 + +cellSize 48 # The cell size used for (RGB) histograms. + # Also dictates the total height of the + # swath of image and associated status data used + # during processing. + # + # swathHeight = 4*cellSize + # + # These in turn dictate the region over which + # connected component analysis can be performed without + # backtracking to fill or outline components. + # The components themselves can in general exceed the + # height of the swath provided initial backtracking has + # occurred. + # Default 48 + +minSample 200 # Suggested minimum sample size for histograms. + # Allows sub sampling if sample size can be exceeded. + # default 200 + +rgbCompThres 10 # Maximum allowed difference in RGB averaged over + # a partial or whole text component to allow snapping + # to black. + # Default 10 + +blackConnThres 120 # Maximum intensity threshold for pixels to be + # considered as part of a text component. + # Default 90 + +blackSnapThres 70 # Maximum intensity threshold for pixels that are + # part of a text component to be snapped to black. + # Default 50 + +blackCompThres 60 # Maximum intensity for averaged pixel values over + # a partial or whole text component to allow snapping + # to black. + # Default 30 + +saturateThres 150 # Minimum intensity for text area pixel to be snapped to + # white if backEnhance enabled. + # default 150 + +sizeThres 48 # Connected component size threshold. + # When connected components recieve contributions + # from more runs than this threshold their stats + # can be checked for satisfaction. + # If so back filling (to the limit of the swath) + # and future forward filling will occur. + # Filling covers snapping and white haloing. + # Default 48 (cell size) + +blackThres 60 # Maximum intensity for histogram peaks to be defined + # as black. + # This is a per intensity resolution unit and hence + # independent of the number of histogram bins. + # Default 60 + +whiteThres 100 # Minimum intensity for histogram peaks to be defined + # as white. + # This is a per intensity resolution unit and hence + # independent of the number of histogram bins. + # Default 100 + +backEnhance False # Boolean on background enhancement to white + # Default True diff --git a/tessdata/configs/api_config b/tessdata/configs/api_config new file mode 100644 index 0000000000..a3f1adabb9 --- /dev/null +++ b/tessdata/configs/api_config @@ -0,0 +1,45 @@ +file_type .bl +expiry_day 20000 +tessedit_make_ep T +tessedit_use_nn F +textord_fast_pitch_test T +tessedit_single_match 0 +newcp_ratings_on 0 +tessedit_zero_rejection T +tessedit_minimal_rejection F +tessedit_write_rep_codes F +ignore_weird_blocks F +tessedit_tweaking_tess_vars T +il1_adaption_test 1 +edges_children_fix T +edges_childarea 0.65 +edges_boxarea 0.9 +mm_fast_clip T +mm_shift_cogs T +mm_debug F +mm_dotcom_enabled F +mm_dotcom_thr 0.2 +mm_test 2 +mm_bl_enabled T +mm_cog_enabled T +mm_junk_offset 1.0 +mm_Il1_enabled T +mm_Il1b_enabled T +mm_Il1_serif_ratio 1.5 +mm_Il1_lr_ratio 2.0 +mm_clean_cog_samples F +mm_use_original F +mm_wiggle_enabled F +mm_truncate_adaption F +tessedit_cp_ratio 2.0 +stopper_numbers_on 3 +permuter_pending_threshold -4.0 +save_doc_words T +doc_dict_enable T +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 +EnableLearning F +acts_fx 0x800 +acts_ocr 0x20 \ No newline at end of file diff --git a/tessdata/configs/api_resaljet b/tessdata/configs/api_resaljet new file mode 100644 index 0000000000..b245d5e424 --- /dev/null +++ b/tessdata/configs/api_resaljet @@ -0,0 +1,34 @@ +file_type .bl +expiry_day 20000 +tessedit_make_ep T +tessedit_use_nn T +textord_fast_pitch_test T +tessedit_single_match 0 +newcp_ratings_on 0 +tessedit_zero_rejection F +tessedit_minimal_rejection F +tessedit_write_rep_codes F +ignore_weird_blocks F +tessedit_tweaking_tess_vars T +il1_adaption_test 1 +edges_children_fix T +edges_childarea 0.65 +edges_boxarea 0.9 +mm_fast_clip T +mm_shift_cogs T +mm_debug F +mm_dotcom_enabled F +mm_dotcom_thr 0.2 +mm_test 2 +mm_bl_enabled T +mm_cog_enabled T +mm_junk_offset 1.0 +mm_Il1_enabled T +mm_Il1b_enabled T +mm_Il1_serif_ratio 1.5 +mm_Il1_lr_ratio 2.0 +mm_clean_cog_samples F +mm_use_original F +mm_wiggle_enabled F +mm_truncate_adaption F +tessedit_cp_ratio 2.0 diff --git a/tessdata/configs/box.train b/tessdata/configs/box.train new file mode 100644 index 0000000000..7b22517151 --- /dev/null +++ b/tessdata/configs/box.train @@ -0,0 +1,16 @@ +file_type .bl +tessedit_use_nn F +textord_fast_pitch_test T +tessedit_single_match 0 +newcp_ratings_on 0 +tessedit_zero_rejection T +tessedit_minimal_rejection F +tessedit_write_rep_codes F +ignore_weird_blocks F +tessedit_tweaking_tess_vars T +il1_adaption_test 1 +edges_children_fix T +edges_childarea 0.65 +edges_boxarea 0.9 +tessedit_resegment_from_boxes T +tessedit_train_from_boxes T diff --git a/tessdata/configs/inter b/tessdata/configs/inter new file mode 100755 index 0000000000..7523fa3ea1 --- /dev/null +++ b/tessdata/configs/inter @@ -0,0 +1,4 @@ +interactive_mode T +edit_variables T +tessedit_draw_words T +tessedit_draw_outwords T diff --git a/tessdata/configs/oldapi_config b/tessdata/configs/oldapi_config new file mode 100644 index 0000000000..9df3a853b3 --- /dev/null +++ b/tessdata/configs/oldapi_config @@ -0,0 +1,36 @@ +file_type .bl +expiry_day 20000 +tessedit_make_ep T +tessedit_use_nn F +textord_fast_pitch_test T +tessedit_single_match 0 +newcp_ratings_on 0 +tessedit_zero_rejection T +tessedit_minimal_rejection F +tessedit_write_rep_codes F +ignore_weird_blocks F +tessedit_tweaking_tess_vars T +il1_adaption_test 1 +edges_children_fix T +edges_childarea 0.65 +edges_boxarea 0.9 +mm_fast_clip T +mm_shift_cogs T +mm_debug F +mm_dotcom_enabled F +mm_dotcom_thr 0.2 +mm_test 2 +mm_bl_enabled T +mm_cog_enabled T +mm_junk_offset 1.0 +mm_Il1_enabled T +mm_Il1b_enabled T +mm_Il1_serif_ratio 1.5 +mm_Il1_lr_ratio 2.0 +mm_clean_cog_samples F +mm_use_original F +mm_wiggle_enabled F +mm_truncate_adaption F +tessedit_cp_ratio 2.0 +stopper_numbers_on 3 +permuter_pending_threshold -4.0 diff --git a/tessdata/configs/oldbox.train b/tessdata/configs/oldbox.train new file mode 100644 index 0000000000..29ca71fe27 --- /dev/null +++ b/tessdata/configs/oldbox.train @@ -0,0 +1,36 @@ +file_type .bl +expiry_day 20000 +tessedit_make_ep T +tessedit_use_nn F +textord_fast_pitch_test T +tessedit_single_match 0 +newcp_ratings_on 0 +tessedit_zero_rejection T +tessedit_minimal_rejection F +tessedit_write_rep_codes F +ignore_weird_blocks F +tessedit_tweaking_tess_vars T +il1_adaption_test 1 +edges_children_fix T +edges_childarea 0.65 +edges_boxarea 0.9 +mm_fast_clip T +mm_shift_cogs T +mm_debug F +mm_dotcom_enabled F +mm_dotcom_thr 0.2 +mm_test 2 +mm_bl_enabled T +mm_cog_enabled T +mm_junk_offset 1.0 +mm_Il1_enabled T +mm_Il1b_enabled T +mm_Il1_serif_ratio 1.5 +mm_Il1_lr_ratio 2.0 +mm_clean_cog_samples F +mm_use_original F +mm_wiggle_enabled F +mm_truncate_adaption F +tessedit_resegment_from_boxes T +tessedit_train_from_boxes T +mm_bl_enabled F diff --git a/tessdata/configs/var_api_config b/tessdata/configs/var_api_config new file mode 100644 index 0000000000..455afefb3a --- /dev/null +++ b/tessdata/configs/var_api_config @@ -0,0 +1,47 @@ +file_type .bl +expiry_day 20000 +tessedit_make_ep T +tessedit_use_nn F +textord_fast_pitch_test T +tessedit_single_match 0 +newcp_ratings_on 0 +tessedit_zero_rejection T +tessedit_minimal_rejection F +tessedit_write_rep_codes F +ignore_weird_blocks F +tessedit_tweaking_tess_vars T +il1_adaption_test 1 +edges_children_fix T +edges_childarea 0.65 +edges_boxarea 0.9 +mm_fast_clip T +mm_shift_cogs T +mm_debug F +mm_dotcom_enabled F +mm_dotcom_thr 0.2 +mm_test 2 +mm_bl_enabled T +mm_cog_enabled T +mm_junk_offset 1.0 +mm_Il1_enabled T +mm_Il1b_enabled T +mm_Il1_serif_ratio 1.5 +mm_Il1_lr_ratio 2.0 +mm_clean_cog_samples F +mm_use_original F +mm_wiggle_enabled F +mm_truncate_adaption F +tessedit_cp_ratio 2.0 +stopper_numbers_on 3 +permuter_pending_threshold -4.0 +save_doc_words T +doc_dict_enable T +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 +EnableLearning F +acts_fx 0x800 +acts_ocr 0x20 +EnableMicroFeatures T +EnableCharNormFeatures T \ No newline at end of file diff --git a/tessdata/configs/var_box.train b/tessdata/configs/var_box.train new file mode 100644 index 0000000000..ab123d35b9 --- /dev/null +++ b/tessdata/configs/var_box.train @@ -0,0 +1,47 @@ +file_type .bl +expiry_day 20000 +tessedit_make_ep T +tessedit_use_nn F +textord_fast_pitch_test T +tessedit_single_match 0 +newcp_ratings_on 0 +tessedit_zero_rejection T +tessedit_minimal_rejection F +tessedit_write_rep_codes F +ignore_weird_blocks F +tessedit_tweaking_tess_vars T +il1_adaption_test 1 +edges_children_fix T +edges_childarea 0.65 +edges_boxarea 0.9 +mm_fast_clip T +mm_shift_cogs T +mm_debug F +mm_dotcom_enabled F +mm_dotcom_thr 0.2 +mm_test 2 +mm_bl_enabled T +mm_cog_enabled T +mm_junk_offset 1.0 +mm_Il1_enabled T +mm_Il1b_enabled T +mm_Il1_serif_ratio 1.5 +mm_Il1_lr_ratio 2.0 +mm_clean_cog_samples F +mm_use_original F +mm_wiggle_enabled F +mm_truncate_adaption F +tessedit_resegment_from_boxes T +tessedit_train_from_boxes T +mm_bl_enabled F +save_doc_words T +doc_dict_enable T +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 +EnableLearning F +acts_fx 0x800 +acts_ocr 0x20 +EnableMicroFeatures T +EnableCharNormFeatures T diff --git a/tessdata/configs/variable_config b/tessdata/configs/variable_config new file mode 100644 index 0000000000..86b271faa2 --- /dev/null +++ b/tessdata/configs/variable_config @@ -0,0 +1,72 @@ +file_type .bl +expiry_day 20000 +tessedit_make_ep T +tessedit_use_nn F +textord_fast_pitch_test T +tessedit_single_match 0 +newcp_ratings_on 0 +tessedit_zero_rejection T +tessedit_minimal_rejection F +tessedit_write_rep_codes F +ignore_weird_blocks F +tessedit_tweaking_tess_vars T +il1_adaption_test 1 +edges_children_fix T +edges_childarea 0.65 +edges_boxarea 0.9 +mm_fast_clip T +mm_shift_cogs T +mm_debug F +mm_dotcom_enabled F +mm_dotcom_thr 0.2 +mm_test 2 +mm_bl_enabled T +mm_cog_enabled T +mm_junk_offset 1.0 +mm_Il1_enabled T +mm_Il1b_enabled T +mm_Il1_serif_ratio 1.5 +mm_Il1_lr_ratio 2.0 +mm_clean_cog_samples F +mm_use_original F +mm_wiggle_enabled F +mm_truncate_adaption F +tessedit_resegment_from_boxes T +tessedit_train_from_boxes T +mm_bl_enabled F +acts_fx 0x800 +acts_ocr 0x20 +RatingScale 30.0 +CertaintyScale 20.0 +MinSlope 0.414213562 +MaxSlope 2.414213562 +EnableAdaptiveMatcher 1 +NormAdjMidpoint 32.0 +NormAdjCurl 2.0 +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 +BuiltInTemplatesFile data/inttemp +BuiltInCutoffsFile data/pffmtable +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 +ReliableConfigThreshold 2 +MinNumPermClasses 3 +GoodAdaptiveMatch 0.125 +GreatAdaptiveMatch 0.0 +EnableIntFX 1 +EnableNewAdaptRules 1 +save_doc_words 1 +doc_dict_enable 1 +ClassPrunerThreshold 229 +ClassPrunerMultiplier 15 +IntegerMatcherMultiplier 7 +IntThetaFudge 128 +CPCutoffStrength 0.15 +EvidenceTableBits 9 +IntEvidenceTruncBits 14 +SEExponentialMultiplier 0 +SimilarityCenter 0.0075 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 \ No newline at end of file diff --git a/tessdata/confsets b/tessdata/confsets new file mode 100755 index 0000000000..6d43f16afe --- /dev/null +++ b/tessdata/confsets @@ -0,0 +1,3 @@ +ao +ft +ce diff --git a/tessdata/fmtable.cls b/tessdata/fmtable.cls new file mode 100755 index 0000000000..bf10f09283 Binary files /dev/null and b/tessdata/fmtable.cls differ diff --git a/tessdata/fnetwts b/tessdata/fnetwts new file mode 100755 index 0000000000..dc9f06295f Binary files /dev/null and b/tessdata/fnetwts differ diff --git a/tessdata/freq-dawg b/tessdata/freq-dawg new file mode 100755 index 0000000000..433e468f70 Binary files /dev/null and b/tessdata/freq-dawg differ diff --git a/tessdata/inttemp b/tessdata/inttemp new file mode 100755 index 0000000000..37835e304d Binary files /dev/null and b/tessdata/inttemp differ diff --git a/tessdata/netwts b/tessdata/netwts new file mode 100755 index 0000000000..0bc501bd51 Binary files /dev/null and b/tessdata/netwts differ diff --git a/tessdata/newdiff.asccodes b/tessdata/newdiff.asccodes new file mode 100755 index 0000000000..95ce309ec4 --- /dev/null +++ b/tessdata/newdiff.asccodes @@ -0,0 +1,51 @@ +a a 304 0 +a c 300 0 +a g 310 0 +a r 260 0 +b s 000 1 +b u 263 0 +b x 374 0 +b w 272 0 +c p 265 0 +c r 246 0 +c t 277 0 +d g 276 0 +e a 305 0 +E a 334 0 +e g 311 0 +e q 323 0 +g D 343 0 +g m 363 0 +g s 357 0 +h d 362 0 +i a 325 0 +i l 322 0 +l d 055 0 +n s 267 0 +o a 306 0 +o b 000 2 +o l 000 3 +p d 273 0 +p g 327 0 +p m 376 0 +p p 274 0 +r g 247 0 +s b 000 4 +s c 275 0 +s p 000 5 +t b 040 0 +t l 274 0 +T M 264 0 +u l 000 6 +u u 317 0 +v t 000 7 +w t 000 8 +1 e 341 0 +1 h 370 0 +1 q 367 0 +3 e 320 0 +3 q 365 0 +3 s 354 0 +5 e 330 0 +5 s 353 0 +7 e 340 0 diff --git a/tessdata/normproto b/tessdata/normproto new file mode 100755 index 0000000000..0b2cc5fc51 --- /dev/null +++ b/tessdata/normproto @@ -0,0 +1,1179 @@ +4 +linear essential -0.250000 0.750000 +linear non-essential 0.000000 1.000000 +linear essential 0.000000 1.000000 +linear non-essential 0.000000 1.000000 + +0 2 +significant elliptical 288 + 0.318846 0.290495 0.195702 0.142236 + 0.000090 0.000282 0.000033 0.000233 +significant elliptical 348 + 0.345446 0.319894 0.213860 0.149429 + 0.000121 0.000359 0.000076 0.000142 + +1 2 +significant elliptical 392 + 0.304545 0.207693 0.232139 0.099862 + 0.000324 0.000385 0.000122 0.000262 +significant elliptical 244 + 0.350724 0.169451 0.212060 0.081235 + 0.000171 0.000159 0.000121 0.000260 + +2 6 +significant elliptical 153 + 0.310424 0.275265 0.215929 0.131091 + 0.000025 0.000883 0.000047 0.000170 +significant elliptical 26 + 0.294140 0.277187 0.210862 0.133591 + 0.000017 0.000334 0.000022 0.000099 +significant elliptical 69 + 0.296168 0.339358 0.194093 0.146740 + 0.000062 0.000246 0.000012 0.000067 +significant elliptical 24 + 0.362177 0.308165 0.243823 0.149779 + 0.000038 0.001627 0.000131 0.000180 +significant elliptical 200 + 0.325442 0.281881 0.226315 0.134555 + 0.000039 0.000978 0.000046 0.000141 +significant elliptical 158 + 0.341765 0.303741 0.233599 0.139534 + 0.000073 0.001316 0.000058 0.000118 + +3 2 +significant elliptical 283 + 0.311447 0.279358 0.209289 0.130291 + 0.000092 0.001479 0.000105 0.000239 +significant elliptical 352 + 0.340944 0.289556 0.227974 0.133208 + 0.000139 0.001152 0.000056 0.000141 + +4 1 +significant elliptical 636 + 0.306944 0.247093 0.190616 0.136465 + 0.000554 0.001132 0.000190 0.000184 + +5 2 +significant elliptical 329 + 0.321523 0.301852 0.208950 0.133222 + 0.000123 0.001405 0.000063 0.000163 +significant elliptical 307 + 0.350979 0.307068 0.231493 0.141976 + 0.000149 0.000906 0.000102 0.000148 + +6 3 +significant elliptical 91 + 0.281702 0.258208 0.189777 0.127966 + 0.000057 0.000399 0.000021 0.000053 +significant elliptical 313 + 0.314844 0.295526 0.202147 0.136239 + 0.000109 0.000660 0.000066 0.000132 +significant elliptical 229 + 0.345528 0.326737 0.217373 0.144567 + 0.000149 0.000685 0.000066 0.000102 + +7 4 +significant elliptical 149 + 0.431994 0.244414 0.224243 0.129643 + 0.000084 0.000096 0.000064 0.000108 +significant elliptical 224 + 0.402071 0.229203 0.218777 0.130081 + 0.000089 0.000179 0.000061 0.000178 +significant elliptical 183 + 0.371363 0.201152 0.208206 0.113405 + 0.000109 0.000205 0.000032 0.000191 +significant elliptical 71 + 0.376610 0.240061 0.187744 0.130601 + 0.000102 0.000133 0.000025 0.000068 + +8 2 +significant elliptical 235 + 0.310908 0.304590 0.200339 0.133824 + 0.000079 0.000666 0.000030 0.000121 +significant elliptical 391 + 0.338318 0.334376 0.216574 0.142083 + 0.000128 0.000873 0.000119 0.000141 + +9 1 +significant elliptical 640 + 0.346514 0.300717 0.206569 0.138103 + 0.000485 0.001384 0.000125 0.000141 + +A 4 +significant elliptical 60 + 0.295317 0.291135 0.197958 0.173142 + 0.000084 0.001030 0.000062 0.000247 +significant elliptical 309 + 0.269117 0.289609 0.210508 0.172803 + 0.000108 0.002740 0.000081 0.000471 +significant elliptical 96 + 0.223682 0.303950 0.194928 0.167332 + 0.000147 0.000433 0.000070 0.000068 +significant elliptical 171 + 0.262423 0.265798 0.186835 0.160956 + 0.000150 0.000873 0.000062 0.000300 + +B 3 +significant elliptical 237 + 0.331420 0.345272 0.216151 0.156055 + 0.000074 0.002468 0.000084 0.000321 +significant elliptical 272 + 0.306253 0.329640 0.202478 0.147694 + 0.000075 0.001864 0.000036 0.000370 +significant elliptical 130 + 0.356162 0.407484 0.235516 0.176441 + 0.000109 0.001776 0.000108 0.000296 + +C 3 +significant elliptical 273 + 0.362437 0.319370 0.242108 0.185750 + 0.000204 0.000858 0.000152 0.000267 +significant elliptical 47 + 0.399123 0.362039 0.256187 0.206893 + 0.000121 0.000761 0.000050 0.000112 +significant elliptical 313 + 0.320724 0.288399 0.210263 0.165915 + 0.000130 0.001137 0.000093 0.000600 + +D 5 +significant elliptical 99 + 0.351165 0.410284 0.244188 0.209579 + 0.000054 0.000118 0.000019 0.000091 +significant elliptical 20 + 0.357471 0.375001 0.228367 0.200333 + 0.000026 0.000074 0.000012 0.000024 +significant elliptical 34 + 0.374195 0.424893 0.256904 0.216720 + 0.000064 0.000540 0.000107 0.000090 +significant elliptical 270 + 0.331887 0.351728 0.217483 0.183960 + 0.000095 0.001013 0.000089 0.000501 +significant elliptical 216 + 0.307222 0.329480 0.204058 0.169656 + 0.000049 0.000669 0.000050 0.000392 + +E 6 +significant elliptical 263 + 0.336000 0.322877 0.229263 0.148080 + 0.000079 0.004662 0.000052 0.000481 +significant elliptical 58 + 0.356175 0.398808 0.240245 0.170828 + 0.000010 0.006082 0.000019 0.000435 +significant elliptical 25 + 0.368631 0.388224 0.245500 0.168056 + 0.000022 0.007474 0.000023 0.000446 +significant elliptical 16 + 0.359910 0.432941 0.256521 0.179215 + 0.000013 0.001808 0.000007 0.000101 +significant elliptical 196 + 0.314755 0.319304 0.211461 0.141241 + 0.000050 0.005128 0.000056 0.000635 +significant elliptical 48 + 0.300947 0.397229 0.197197 0.157860 + 0.000059 0.001248 0.000026 0.000168 + +F 4 +significant elliptical 35 + 0.389726 0.217505 0.199265 0.122331 + 0.000055 0.000048 0.000034 0.000092 +significant elliptical 280 + 0.372756 0.250254 0.201145 0.136829 + 0.000185 0.002175 0.000081 0.000517 +significant elliptical 104 + 0.334166 0.314517 0.199804 0.152319 + 0.000056 0.000924 0.000054 0.000241 +significant elliptical 193 + 0.409500 0.325012 0.226656 0.166058 + 0.000153 0.002850 0.000152 0.000297 + +G 4 +significant elliptical 126 + 0.334257 0.354859 0.221870 0.187992 + 0.000119 0.000389 0.000128 0.000519 +significant elliptical 301 + 0.307960 0.345713 0.197583 0.168543 + 0.000086 0.001279 0.000056 0.000561 +significant elliptical 21 + 0.331917 0.400075 0.207798 0.195615 + 0.000054 0.000313 0.000069 0.000043 +significant elliptical 191 + 0.360213 0.411850 0.236862 0.205326 + 0.000213 0.000524 0.000067 0.000097 + +H 2 +significant elliptical 438 + 0.323232 0.337900 0.207226 0.181311 + 0.000309 0.002471 0.000150 0.000731 +significant elliptical 128 + 0.359868 0.457742 0.250487 0.217008 + 0.000184 0.000990 0.000103 0.000162 + +I 4 +significant elliptical 446 + 0.322057 0.185283 0.218852 0.088663 + 0.000169 0.001594 0.000193 0.000877 +significant elliptical 53 + 0.356535 0.175547 0.226608 0.085736 + 0.000064 0.000566 0.000062 0.000401 +significant elliptical 116 + 0.356625 0.220825 0.256705 0.107650 + 0.000102 0.000283 0.000047 0.000289 +significant elliptical 22 + 0.384536 0.227324 0.269152 0.121914 + 0.000118 0.000125 0.000083 0.000156 + +J 7 +significant elliptical 17 + 0.389382 0.214380 0.259519 0.109430 + 0.000542 0.001663 0.000481 0.001308 +significant elliptical 78 + 0.273468 0.210507 0.261378 0.101218 + 0.000275 0.001177 0.000190 0.000648 +significant elliptical 86 + 0.308849 0.262402 0.209846 0.160450 + 0.000116 0.000970 0.000057 0.000642 +significant elliptical 68 + 0.322921 0.262029 0.232657 0.143799 + 0.000128 0.001955 0.000117 0.000887 +significant elliptical 75 + 0.336729 0.273457 0.242610 0.149696 + 0.000230 0.002835 0.000046 0.001711 +significant elliptical 207 + 0.281798 0.228827 0.205787 0.138667 + 0.000083 0.001209 0.000031 0.000527 +significant elliptical 42 + 0.255344 0.205205 0.203199 0.127943 + 0.000043 0.000501 0.000064 0.000679 + +K 2 +significant elliptical 133 + 0.361854 0.427578 0.256950 0.199465 + 0.000201 0.002140 0.000086 0.000139 +significant elliptical 502 + 0.324520 0.323561 0.220265 0.164182 + 0.000248 0.003542 0.000152 0.000598 + +L 3 +significant elliptical 155 + 0.292148 0.290278 0.249502 0.166207 + 0.000155 0.001431 0.000094 0.000539 +significant elliptical 22 + 0.233466 0.177916 0.197641 0.099307 + 0.000025 0.000072 0.000041 0.000103 +significant elliptical 461 + 0.264958 0.227055 0.215267 0.126636 + 0.000160 0.002218 0.000086 0.000973 + +M 1 +significant elliptical 633 + 0.305688 0.439220 0.206972 0.227820 + 0.000380 0.007449 0.000287 0.001094 + +N 2 +significant elliptical 483 + 0.325608 0.351061 0.200671 0.184343 + 0.000242 0.002271 0.000134 0.000696 +significant elliptical 134 + 0.377818 0.427549 0.230610 0.226265 + 0.000141 0.001021 0.000102 0.000215 + +O 4 +significant elliptical 66 + 0.331905 0.367717 0.222514 0.206667 + 0.000034 0.000210 0.000014 0.000196 +significant elliptical 153 + 0.324999 0.325504 0.205302 0.176982 + 0.000080 0.000854 0.000030 0.000758 +significant elliptical 174 + 0.306470 0.307732 0.193817 0.168686 + 0.000058 0.000159 0.000024 0.000272 +significant elliptical 247 + 0.356705 0.381356 0.232853 0.209108 + 0.000164 0.000409 0.000073 0.000221 + +P 6 +significant elliptical 197 + 0.391887 0.273217 0.196445 0.156036 + 0.000151 0.000425 0.000128 0.000229 +significant elliptical 147 + 0.404152 0.336509 0.228668 0.174680 + 0.000215 0.000687 0.000110 0.000220 +significant elliptical 108 + 0.365895 0.236510 0.184232 0.133395 + 0.000049 0.000326 0.000052 0.000205 +significant elliptical 116 + 0.351031 0.311655 0.202745 0.162444 + 0.000093 0.000277 0.000031 0.000119 +significant elliptical 22 + 0.349532 0.242767 0.178135 0.132613 + 0.000058 0.000424 0.000030 0.000228 +significant elliptical 36 + 0.323145 0.294749 0.199192 0.154595 + 0.000039 0.000117 0.000025 0.000051 + +Q 6 +significant elliptical 145 + 0.247434 0.376216 0.231609 0.174751 + 0.000216 0.000397 0.000069 0.000192 +significant elliptical 213 + 0.293870 0.442806 0.262442 0.209334 + 0.000227 0.002002 0.000119 0.000208 +significant elliptical 58 + 0.295001 0.375931 0.202084 0.183128 + 0.000126 0.003478 0.000023 0.000870 +significant elliptical 103 + 0.319993 0.349013 0.208729 0.168949 + 0.000087 0.004217 0.000029 0.001194 +significant elliptical 67 + 0.334749 0.356339 0.226434 0.188035 + 0.000094 0.000879 0.000056 0.000605 +significant elliptical 39 + 0.243414 0.507446 0.292352 0.203621 + 0.000073 0.000939 0.000272 0.000084 + +R 2 +significant elliptical 303 + 0.344237 0.370775 0.227930 0.174130 + 0.000192 0.002895 0.000191 0.000347 +significant elliptical 322 + 0.309842 0.323667 0.204315 0.152571 + 0.000165 0.001731 0.000072 0.000426 + +S 3 +significant elliptical 29 + 0.386699 0.367816 0.249920 0.164740 + 0.000063 0.004082 0.000177 0.000462 +significant elliptical 200 + 0.353651 0.340052 0.238898 0.153711 + 0.000095 0.003130 0.000071 0.000414 +significant elliptical 391 + 0.321460 0.306240 0.211646 0.138986 + 0.000204 0.001845 0.000169 0.000394 + +T 2 +significant elliptical 340 + 0.373288 0.252468 0.207323 0.134260 + 0.000417 0.003913 0.000044 0.000906 +significant elliptical 299 + 0.428225 0.283477 0.234075 0.159355 + 0.000242 0.003124 0.000177 0.000464 + +U 5 +significant elliptical 141 + 0.384893 0.393738 0.243227 0.209320 + 0.000157 0.000420 0.000079 0.000157 +significant elliptical 70 + 0.305733 0.286175 0.196826 0.150917 + 0.000043 0.000116 0.000022 0.000388 +significant elliptical 37 + 0.336843 0.308984 0.199727 0.162204 + 0.000015 0.000583 0.000010 0.000310 +significant elliptical 160 + 0.322979 0.313158 0.209934 0.181585 + 0.000053 0.000406 0.000076 0.000700 +significant elliptical 216 + 0.353749 0.344881 0.215413 0.187599 + 0.000103 0.000435 0.000072 0.000270 + +V 8 +significant elliptical 79 + 0.377781 0.266237 0.189623 0.159885 + 0.000013 0.000639 0.000035 0.000476 +significant elliptical 122 + 0.362693 0.245200 0.188026 0.146523 + 0.000034 0.000652 0.000037 0.000655 +significant elliptical 28 + 0.386884 0.249268 0.194369 0.165236 + 0.000031 0.000158 0.000025 0.000401 +significant elliptical 57 + 0.345545 0.241030 0.180924 0.140658 + 0.000027 0.000392 0.000035 0.000626 +significant elliptical 174 + 0.398155 0.286342 0.192975 0.168610 + 0.000071 0.000250 0.000059 0.000128 +significant elliptical 58 + 0.423963 0.289968 0.202626 0.173805 + 0.000077 0.000263 0.000036 0.000093 +significant elliptical 22 + 0.481122 0.330803 0.224107 0.189034 + 0.000090 0.000232 0.000037 0.000083 +significant elliptical 97 + 0.452654 0.317746 0.214176 0.178190 + 0.000079 0.000263 0.000020 0.000087 + +W 4 +significant elliptical 155 + 0.430206 0.491588 0.220413 0.238618 + 0.000399 0.001052 0.000081 0.000289 +significant elliptical 234 + 0.340371 0.371117 0.192385 0.199310 + 0.000130 0.004933 0.000104 0.001401 +significant elliptical 59 + 0.390083 0.452136 0.199475 0.237455 + 0.000039 0.000414 0.000070 0.000759 +significant elliptical 169 + 0.368663 0.429648 0.194138 0.228796 + 0.000040 0.001544 0.000114 0.000934 + +X 4 +significant elliptical 405 + 0.314408 0.309117 0.223767 0.162894 + 0.000126 0.003023 0.000068 0.000620 +significant elliptical 28 + 0.384005 0.399214 0.275533 0.190603 + 0.000055 0.000896 0.000055 0.000214 +significant elliptical 108 + 0.354567 0.402094 0.261455 0.193293 + 0.000099 0.001357 0.000056 0.000130 +significant elliptical 96 + 0.341968 0.319510 0.238352 0.177133 + 0.000064 0.001362 0.000045 0.000257 + +Y 2 +significant elliptical 246 + 0.418942 0.295175 0.236138 0.163411 + 0.000223 0.001797 0.000276 0.000143 +significant elliptical 386 + 0.379923 0.246897 0.205108 0.141586 + 0.000295 0.001904 0.000170 0.000490 + +Z 4 +significant elliptical 272 + 0.334833 0.318358 0.242311 0.158239 + 0.000095 0.002135 0.000069 0.000397 +significant elliptical 73 + 0.360911 0.374730 0.260379 0.178154 + 0.000064 0.001297 0.000090 0.000180 +significant elliptical 199 + 0.312178 0.279416 0.220893 0.134996 + 0.000074 0.002950 0.000055 0.000443 +significant elliptical 86 + 0.303930 0.334053 0.204256 0.151017 + 0.000051 0.000500 0.000012 0.000101 + +a 1 +significant elliptical 637 + 0.233354 0.251547 0.161230 0.142984 + 0.000199 0.001258 0.000069 0.000415 + +& 3 +significant elliptical 262 + 0.315604 0.398104 0.218209 0.179905 + 0.000128 0.006317 0.000091 0.000399 +significant elliptical 226 + 0.284468 0.314591 0.205687 0.153445 + 0.000141 0.003791 0.000067 0.000566 +significant elliptical 137 + 0.273864 0.340313 0.181402 0.174428 + 0.000088 0.002015 0.000030 0.000588 + +* 5 +significant elliptical 103 + 0.450644 0.132755 0.114076 0.116726 + 0.000056 0.000208 0.000148 0.000179 +significant elliptical 71 + 0.419567 0.137041 0.115418 0.116413 + 0.000135 0.000063 0.000045 0.000048 +significant elliptical 100 + 0.518593 0.108533 0.091930 0.087753 + 0.000208 0.000549 0.000054 0.000126 +significant elliptical 225 + 0.481850 0.157880 0.122050 0.123796 + 0.000100 0.000796 0.000122 0.000161 +significant elliptical 127 + 0.511726 0.164415 0.122738 0.124260 + 0.000083 0.001016 0.000118 0.000173 + +@ 7 +significant elliptical 30 + 0.317959 0.429413 0.228978 0.221312 + 0.000042 0.040083 0.000111 0.000471 +significant elliptical 270 + 0.322822 0.458187 0.189195 0.169200 + 0.000074 0.006241 0.000061 0.000130 +significant elliptical 197 + 0.350188 0.501287 0.204643 0.186594 + 0.000103 0.010684 0.000043 0.000177 +significant elliptical 45 + 0.358445 0.322309 0.251813 0.235711 + 0.000426 0.010659 0.000245 0.000482 +significant elliptical 16 + 0.386341 0.633730 0.228163 0.214108 + 0.000060 0.000866 0.000060 0.000148 +significant elliptical 44 + 0.366754 0.585792 0.212737 0.196277 + 0.000048 0.008805 0.000023 0.000047 +significant elliptical 31 + 0.342910 0.662457 0.239315 0.229741 + 0.000088 0.001355 0.000082 0.000204 + +b 2 +significant elliptical 384 + 0.283991 0.288488 0.193843 0.147596 + 0.000125 0.000583 0.000072 0.000294 +significant elliptical 250 + 0.312248 0.309609 0.218169 0.150781 + 0.000157 0.000558 0.000112 0.000353 + +\ 3 +significant elliptical 272 + 0.363305 0.172220 0.226357 0.102810 + 0.000179 0.000063 0.000064 0.000462 +significant elliptical 131 + 0.310157 0.144095 0.193624 0.075187 + 0.000038 0.000016 0.000027 0.000270 +significant elliptical 227 + 0.326673 0.155965 0.211055 0.078384 + 0.000094 0.000082 0.000101 0.000393 + +! 4 +significant elliptical 160 + 0.346216 0.165118 0.225065 0.065635 + 0.000039 0.000066 0.000038 0.000194 +significant elliptical 56 + 0.366786 0.172613 0.235305 0.074989 + 0.000059 0.000106 0.000061 0.000213 +significant elliptical 270 + 0.303881 0.152900 0.199710 0.058633 + 0.000118 0.000101 0.000046 0.000108 +significant elliptical 143 + 0.327683 0.158261 0.210570 0.064868 + 0.000032 0.000087 0.000063 0.000179 + +c 1 +significant elliptical 639 + 0.248247 0.222283 0.160367 0.134508 + 0.000156 0.000444 0.000037 0.000268 + +: 2 +significant elliptical 291 + 0.216809 0.103314 0.152953 0.057817 + 0.000069 0.000357 0.000065 0.000163 +significant elliptical 342 + 0.242681 0.110191 0.173392 0.064314 + 0.000101 0.000263 0.000115 0.000130 + +, 5 +significant elliptical 294 + 0.021151 0.080123 0.095117 0.062497 + 0.000098 0.000099 0.000125 0.000127 +significant elliptical 85 + 0.049466 0.081748 0.095288 0.069758 + 0.000098 0.000072 0.000084 0.000156 +significant elliptical 50 + 0.039684 0.057450 0.066739 0.049777 + 0.000092 0.000030 0.000046 0.000038 +significant elliptical 30 + -0.022895 0.090486 0.103946 0.065098 + 0.000044 0.000134 0.000096 0.000148 +significant elliptical 84 + -0.003030 0.080111 0.094504 0.058128 + 0.000034 0.000113 0.000097 0.000099 + +d 3 +significant elliptical 20 + 0.340370 0.340991 0.236804 0.174992 + 0.000054 0.000985 0.000050 0.000194 +significant elliptical 388 + 0.309519 0.313876 0.211977 0.160371 + 0.000137 0.000976 0.000134 0.000214 +significant elliptical 226 + 0.276437 0.281996 0.188853 0.146486 + 0.000139 0.000463 0.000026 0.000242 + +$ 6 +significant elliptical 43 + 0.399100 0.329827 0.246828 0.136272 + 0.000358 0.004925 0.000106 0.000071 +significant elliptical 193 + 0.342356 0.330095 0.246215 0.134128 + 0.000394 0.005918 0.000258 0.000216 +significant elliptical 203 + 0.329310 0.230339 0.199154 0.113864 + 0.000250 0.001755 0.000096 0.000150 +significant elliptical 40 + 0.321435 0.381088 0.241895 0.138996 + 0.000020 0.001780 0.000009 0.000102 +significant elliptical 113 + 0.310198 0.340346 0.228925 0.136503 + 0.000095 0.003308 0.000039 0.000205 +significant elliptical 48 + 0.296014 0.373516 0.217431 0.137046 + 0.000067 0.003154 0.000055 0.000082 + +. 2 +significant elliptical 21 + 0.124395 0.058629 0.062725 0.061468 + 0.000281 0.000070 0.000078 0.000099 +significant elliptical 618 + 0.078244 0.051716 0.054854 0.054216 + 0.000200 0.000082 0.000086 0.000144 + +e 1 +significant elliptical 625 + 0.244195 0.244558 0.154216 0.132900 + 0.000174 0.000806 0.000051 0.000211 + += 3 +significant elliptical 42 + 0.201548 0.225105 0.114890 0.166300 + 0.000348 0.000820 0.000115 0.000163 +significant elliptical 38 + 0.267091 0.223469 0.126144 0.180550 + 0.000046 0.001845 0.000022 0.000105 +significant elliptical 475 + 0.261742 0.219701 0.100742 0.157173 + 0.000221 0.000493 0.000087 0.000160 + +f 11 +significant elliptical 39 + 0.271675 0.280558 0.303388 0.163255 + 0.000087 0.000163 0.000197 0.000112 +significant elliptical 70 + 0.317847 0.228550 0.213079 0.122138 + 0.000037 0.001338 0.000067 0.000504 +significant elliptical 23 + 0.359566 0.260323 0.239966 0.131342 + 0.000019 0.001076 0.000048 0.000434 +significant elliptical 21 + 0.345946 0.275043 0.236450 0.139564 + 0.000013 0.000988 0.000011 0.000341 +significant elliptical 28 + 0.336082 0.280577 0.227341 0.143115 + 0.000009 0.000355 0.000015 0.000294 +significant elliptical 67 + 0.333286 0.184281 0.204272 0.097052 + 0.000035 0.000661 0.000027 0.000309 +significant elliptical 19 + 0.372940 0.238719 0.235006 0.106585 + 0.000014 0.000192 0.000013 0.000056 +significant elliptical 86 + 0.364220 0.191834 0.215868 0.102239 + 0.000084 0.000308 0.000053 0.000266 +significant elliptical 133 + 0.364672 0.186794 0.194268 0.101954 + 0.000197 0.000203 0.000042 0.000342 +significant elliptical 107 + 0.401069 0.212153 0.234294 0.108049 + 0.000226 0.000617 0.000152 0.000248 +significant elliptical 41 + 0.341583 0.271438 0.283773 0.144464 + 0.000228 0.000219 0.000099 0.000281 + +g 1 +significant elliptical 604 + 0.168422 0.329618 0.210773 0.146473 + 0.000703 0.002074 0.000156 0.000325 + +` 8 +significant elliptical 48 + 0.580671 0.056521 0.059270 0.057491 + 0.000133 0.000072 0.000105 0.000140 +significant elliptical 74 + 0.584962 0.081105 0.093365 0.060219 + 0.000132 0.000051 0.000074 0.000060 +significant elliptical 40 + 0.559296 0.101706 0.116034 0.073287 + 0.000089 0.000026 0.000035 0.000036 +significant elliptical 100 + 0.480698 0.072876 0.090075 0.050723 + 0.000232 0.000047 0.000076 0.000069 +significant elliptical 86 + 0.543431 0.054620 0.061542 0.049971 + 0.000134 0.000064 0.000115 0.000098 +significant elliptical 21 + 0.554305 0.072779 0.084105 0.057668 + 0.000042 0.000008 0.000018 0.000060 +significant elliptical 65 + 0.523189 0.069279 0.082231 0.057370 + 0.000084 0.000019 0.000030 0.000090 +significant elliptical 40 + 0.526960 0.093796 0.108069 0.066044 + 0.000083 0.000069 0.000058 0.000038 + +> 4 +significant elliptical 98 + 0.281632 0.222804 0.157402 0.164141 + 0.000062 0.000102 0.000039 0.000068 +significant elliptical 174 + 0.235604 0.181000 0.131518 0.134828 + 0.000075 0.000162 0.000043 0.000117 +significant elliptical 287 + 0.256385 0.202272 0.143844 0.148978 + 0.000080 0.000140 0.000042 0.000098 +significant elliptical 80 + 0.315982 0.177652 0.193077 0.105489 + 0.000132 0.000029 0.000047 0.000034 + +h 5 +significant elliptical 91 + 0.333944 0.305505 0.231239 0.152469 + 0.000130 0.000664 0.000120 0.000088 +significant elliptical 193 + 0.287168 0.259506 0.188408 0.141111 + 0.000129 0.000192 0.000042 0.000166 +significant elliptical 88 + 0.250656 0.289334 0.190826 0.148177 + 0.000063 0.000507 0.000020 0.000088 +significant elliptical 55 + 0.267381 0.314258 0.204407 0.160264 + 0.000021 0.000419 0.000029 0.000163 +significant elliptical 208 + 0.298389 0.313250 0.218687 0.155580 + 0.000154 0.000721 0.000095 0.000281 + +# 1 +significant elliptical 633 + 0.333119 0.306886 0.205136 0.144635 + 0.000439 0.001878 0.000371 0.000298 + +i 3 +significant elliptical 59 + 0.290579 0.241577 0.228792 0.119944 + 0.000069 0.000074 0.000039 0.000117 +significant elliptical 127 + 0.294744 0.166868 0.199790 0.064340 + 0.000223 0.000268 0.000067 0.000144 +significant elliptical 445 + 0.340434 0.177919 0.213800 0.072450 + 0.000267 0.000610 0.000242 0.000264 + +j 6 +significant elliptical 76 + 0.193618 0.219655 0.249009 0.102132 + 0.000128 0.000096 0.000028 0.000224 +significant elliptical 154 + 0.246919 0.247668 0.283461 0.108393 + 0.000120 0.000708 0.000069 0.000663 +significant elliptical 254 + 0.248374 0.214245 0.253480 0.091694 + 0.000301 0.000779 0.000099 0.000639 +significant elliptical 67 + 0.212235 0.228824 0.276882 0.090111 + 0.000152 0.000165 0.000051 0.000332 +significant elliptical 22 + 0.224574 0.257417 0.305783 0.128845 + 0.000120 0.000105 0.000064 0.000212 +significant elliptical 47 + 0.288986 0.238297 0.286240 0.102478 + 0.000355 0.000745 0.000381 0.000716 + +k 2 +significant elliptical 499 + 0.291232 0.279034 0.205081 0.138877 + 0.000312 0.001797 0.000153 0.000365 +significant elliptical 138 + 0.340772 0.292329 0.235794 0.145799 + 0.000310 0.001498 0.000117 0.000201 + +l 9 +significant elliptical 26 + 0.410063 0.185063 0.254191 0.079622 + 0.000088 0.000083 0.000117 0.000083 +significant elliptical 126 + 0.372518 0.187673 0.243312 0.076234 + 0.000145 0.000315 0.000098 0.000144 +significant elliptical 84 + 0.336687 0.198974 0.241152 0.081915 + 0.000125 0.000511 0.000076 0.000386 +significant elliptical 112 + 0.350419 0.160249 0.212562 0.070170 + 0.000142 0.000056 0.000046 0.000143 +significant elliptical 27 + 0.317483 0.172352 0.221586 0.071099 + 0.000031 0.000084 0.000019 0.000120 +significant elliptical 76 + 0.322225 0.147454 0.203912 0.061106 + 0.000033 0.000028 0.000028 0.000127 +significant elliptical 121 + 0.296051 0.158236 0.205481 0.064890 + 0.000082 0.000177 0.000071 0.000160 +significant elliptical 31 + 0.303626 0.238079 0.244653 0.120279 + 0.000029 0.000169 0.000024 0.000159 +significant elliptical 34 + 0.287216 0.235816 0.235344 0.122200 + 0.000035 0.000038 0.000011 0.000078 + +{ 7 +significant elliptical 29 + 0.398345 0.203188 0.267112 0.090245 + 0.000260 0.000086 0.000102 0.000121 +significant elliptical 173 + 0.291204 0.184886 0.240086 0.073775 + 0.000117 0.000167 0.000142 0.000245 +significant elliptical 81 + 0.264736 0.172055 0.227416 0.063600 + 0.000049 0.000095 0.000057 0.000217 +significant elliptical 62 + 0.302109 0.210209 0.269688 0.093870 + 0.000160 0.000111 0.000139 0.000166 +significant elliptical 86 + 0.342628 0.204155 0.267689 0.088644 + 0.000245 0.000116 0.000143 0.000161 +significant elliptical 71 + 0.334882 0.170782 0.227213 0.066081 + 0.000304 0.000042 0.000043 0.000143 +significant elliptical 137 + 0.263040 0.227921 0.290832 0.095109 + 0.000292 0.000212 0.000184 0.000418 + +< 3 +significant elliptical 335 + 0.250990 0.189502 0.138358 0.140789 + 0.000106 0.000193 0.000067 0.000115 +significant elliptical 227 + 0.279632 0.216093 0.154837 0.159516 + 0.000130 0.000210 0.000067 0.000175 +significant elliptical 77 + 0.333171 0.185913 0.192668 0.114284 + 0.000203 0.000126 0.000065 0.000129 + +( 6 +significant elliptical 165 + 0.290294 0.188119 0.247744 0.082120 + 0.000161 0.000107 0.000184 0.000108 +significant elliptical 108 + 0.260563 0.187189 0.227544 0.094273 + 0.000104 0.000187 0.000065 0.000227 +significant elliptical 149 + 0.332481 0.165451 0.218673 0.076262 + 0.000269 0.000073 0.000102 0.000119 +significant elliptical 90 + 0.336951 0.195401 0.258480 0.086412 + 0.000197 0.000064 0.000058 0.000154 +significant elliptical 97 + 0.389052 0.188168 0.245315 0.088715 + 0.000408 0.000156 0.000272 0.000184 +significant elliptical 22 + 0.461612 0.197903 0.261322 0.089354 + 0.000293 0.000121 0.000166 0.000140 + +[ 2 +significant elliptical 99 + 0.357152 0.203227 0.254760 0.091556 + 0.000384 0.000227 0.000134 0.000335 +significant elliptical 452 + 0.297166 0.210485 0.268712 0.084354 + 0.000607 0.000342 0.000408 0.000318 + +m 5 +significant elliptical 29 + 0.233528 0.296680 0.184001 0.211086 + 0.000130 0.010952 0.000046 0.000869 +significant elliptical 128 + 0.259874 0.373509 0.159451 0.231394 + 0.000025 0.000638 0.000020 0.000287 +significant elliptical 240 + 0.241860 0.359991 0.160016 0.215954 + 0.000044 0.001232 0.000051 0.000746 +significant elliptical 66 + 0.211034 0.409874 0.158716 0.222350 + 0.000031 0.000431 0.000015 0.000190 +significant elliptical 153 + 0.225164 0.372966 0.160210 0.204914 + 0.000015 0.002240 0.000052 0.001039 + +- 5 +significant elliptical 51 + 0.227232 0.062052 0.045462 0.078213 + 0.000038 0.000032 0.000056 0.000048 +significant elliptical 173 + 0.248704 0.061441 0.043531 0.078484 + 0.000052 0.000020 0.000026 0.000037 +significant elliptical 109 + 0.240713 0.076562 0.062603 0.093625 + 0.000063 0.000025 0.000031 0.000052 +significant elliptical 193 + 0.273098 0.076724 0.050509 0.099693 + 0.000092 0.000272 0.000080 0.000589 +significant elliptical 111 + 0.314401 0.068818 0.045223 0.089966 + 0.000256 0.000442 0.000123 0.000891 + +n 1 +significant elliptical 636 + 0.239332 0.257083 0.162513 0.154298 + 0.000227 0.000781 0.000070 0.000200 + +o 2 +significant elliptical 70 + 0.270632 0.254284 0.164075 0.155057 + 0.000034 0.000348 0.000029 0.000202 +significant elliptical 566 + 0.244845 0.245970 0.156272 0.144596 + 0.000082 0.000305 0.000037 0.000233 + +p 3 +significant elliptical 267 + 0.183653 0.316133 0.209733 0.162461 + 0.000151 0.000617 0.000058 0.000225 +significant elliptical 231 + 0.198747 0.277798 0.186358 0.147656 + 0.000119 0.000735 0.000050 0.000306 +significant elliptical 100 + 0.229948 0.292703 0.199403 0.158464 + 0.000177 0.000821 0.000154 0.000273 + +% 5 +significant elliptical 21 + 0.358987 0.307843 0.237889 0.195256 + 0.000074 0.008177 0.000077 0.001547 +significant elliptical 97 + 0.327262 0.261495 0.224972 0.159225 + 0.000164 0.004804 0.000046 0.001582 +significant elliptical 109 + 0.361352 0.545608 0.208876 0.205846 + 0.000118 0.002669 0.000051 0.000185 +significant elliptical 235 + 0.338706 0.467290 0.198261 0.185091 + 0.000067 0.006953 0.000047 0.000602 +significant elliptical 178 + 0.314620 0.434499 0.189620 0.178236 + 0.000085 0.002948 0.000062 0.000493 + ++ 1 +significant elliptical 625 + 0.262680 0.173990 0.135041 0.130994 + 0.000395 0.000232 0.000152 0.000139 + +q 3 +significant elliptical 35 + 0.246861 0.277601 0.193582 0.149681 + 0.000184 0.000990 0.000141 0.000285 +significant elliptical 161 + 0.209216 0.268783 0.186384 0.139683 + 0.000089 0.000608 0.000038 0.000280 +significant elliptical 330 + 0.181605 0.308012 0.205872 0.151479 + 0.000177 0.000623 0.000115 0.000240 + +? 3 +significant elliptical 377 + 0.360372 0.208830 0.205193 0.103750 + 0.000202 0.000373 0.000110 0.000223 +significant elliptical 78 + 0.367927 0.241648 0.174797 0.117292 + 0.000092 0.000058 0.000017 0.000030 +significant elliptical 169 + 0.410893 0.243463 0.224818 0.102440 + 0.000238 0.000520 0.000053 0.000091 + +' 4 +significant elliptical 169 + 0.570494 0.071590 0.090491 0.051550 + 0.000181 0.000125 0.000195 0.000171 +significant elliptical 60 + 0.608460 0.063110 0.079688 0.045543 + 0.000174 0.000056 0.000077 0.000095 +significant elliptical 202 + 0.530821 0.063473 0.077768 0.049788 + 0.000150 0.000166 0.000174 0.000301 +significant elliptical 204 + 0.487237 0.077494 0.094782 0.058817 + 0.000227 0.000037 0.000068 0.000079 + +r 4 +significant elliptical 53 + 0.273045 0.227264 0.185012 0.153642 + 0.000033 0.001142 0.000019 0.000615 +significant elliptical 352 + 0.265015 0.173308 0.164367 0.115196 + 0.000085 0.001569 0.000075 0.000715 +significant elliptical 193 + 0.289524 0.156183 0.162701 0.113876 + 0.000043 0.000392 0.000065 0.000403 +significant elliptical 34 + 0.306912 0.163010 0.169389 0.125540 + 0.000045 0.000221 0.000047 0.000297 + +} 5 +significant elliptical 172 + 0.250847 0.222965 0.283932 0.092085 + 0.000282 0.000237 0.000245 0.000385 +significant elliptical 201 + 0.270419 0.177619 0.232381 0.068713 + 0.000186 0.000158 0.000061 0.000273 +significant elliptical 118 + 0.302050 0.198324 0.259131 0.080625 + 0.000146 0.000076 0.000056 0.000213 +significant elliptical 71 + 0.329161 0.174648 0.228855 0.070712 + 0.000141 0.000035 0.000034 0.000115 +significant elliptical 62 + 0.353735 0.202930 0.263197 0.084049 + 0.000481 0.000958 0.000112 0.000415 + +) 7 +significant elliptical 52 + 0.341344 0.194430 0.259021 0.086611 + 0.000144 0.000060 0.000084 0.000152 +significant elliptical 144 + 0.331478 0.169722 0.225193 0.076676 + 0.000185 0.000054 0.000060 0.000142 +significant elliptical 115 + 0.289713 0.174047 0.230087 0.075603 + 0.000189 0.000083 0.000132 0.000099 +significant elliptical 92 + 0.275550 0.197463 0.260401 0.086004 + 0.000096 0.000090 0.000153 0.000074 +significant elliptical 83 + 0.305625 0.195463 0.257930 0.085290 + 0.000078 0.000051 0.000052 0.000140 +significant elliptical 124 + 0.250793 0.187394 0.227722 0.095209 + 0.000104 0.000124 0.000042 0.000136 +significant elliptical 29 + 0.387880 0.192209 0.256911 0.082400 + 0.000130 0.000063 0.000096 0.000110 + +] 5 +significant elliptical 41 + 0.427015 0.219709 0.280652 0.087408 + 0.000386 0.000148 0.000126 0.000241 +significant elliptical 86 + 0.365446 0.204988 0.260486 0.086787 + 0.000191 0.000184 0.000152 0.000286 +significant elliptical 16 + 0.396721 0.182680 0.237607 0.068525 + 0.000204 0.000279 0.000197 0.000211 +significant elliptical 297 + 0.306760 0.213303 0.270200 0.087420 + 0.000444 0.000221 0.000363 0.000283 +significant elliptical 112 + 0.270863 0.190238 0.244829 0.074670 + 0.000216 0.000159 0.000079 0.000256 + +s 1 +significant elliptical 637 + 0.244088 0.228902 0.161654 0.120423 + 0.000133 0.001159 0.000058 0.000308 + +; 2 +significant elliptical 21 + 0.260456 0.131133 0.194874 0.075360 + 0.000182 0.000390 0.000176 0.000430 +significant elliptical 539 + 0.165605 0.131213 0.197472 0.068188 + 0.000641 0.000441 0.000240 0.000256 + +/ 6 +significant elliptical 85 + 0.267252 0.177119 0.230006 0.110530 + 0.000185 0.000079 0.000077 0.000494 +significant elliptical 220 + 0.342780 0.171268 0.222184 0.107073 + 0.000089 0.000096 0.000056 0.000713 +significant elliptical 28 + 0.314043 0.174559 0.230352 0.102568 + 0.000091 0.000028 0.000076 0.000233 +significant elliptical 73 + 0.316247 0.156535 0.201314 0.103781 + 0.000082 0.000059 0.000034 0.000349 +significant elliptical 82 + 0.322551 0.194536 0.248478 0.129960 + 0.000183 0.000117 0.000298 0.000531 +significant elliptical 128 + 0.378140 0.180570 0.238258 0.105054 + 0.000181 0.000116 0.000138 0.000446 + +t 2 +significant elliptical 583 + 0.291134 0.190937 0.193078 0.100389 + 0.000255 0.000828 0.000088 0.000366 +significant elliptical 57 + 0.339090 0.174326 0.194378 0.084102 + 0.000116 0.000414 0.000141 0.000429 + +~ 2 +significant elliptical 320 + 0.261140 0.116162 0.058461 0.148148 + 0.000519 0.000070 0.000080 0.000103 +significant elliptical 80 + 0.581353 0.086597 0.053718 0.109318 + 0.000745 0.000033 0.000079 0.000052 + +u 3 +significant elliptical 405 + 0.234926 0.241295 0.156408 0.146973 + 0.000069 0.000508 0.000036 0.000156 +significant elliptical 201 + 0.255415 0.263480 0.167510 0.161020 + 0.000073 0.000390 0.000027 0.000149 +significant elliptical 24 + 0.210606 0.219534 0.146129 0.137077 + 0.000018 0.000192 0.000048 0.000186 + +_ 9 +significant elliptical 83 + -0.176219 0.107712 0.037824 0.150783 + 0.000758 0.000075 0.000143 0.000129 +significant elliptical 124 + -0.089273 0.120124 0.050328 0.166633 + 0.000095 0.000170 0.000033 0.000369 +significant elliptical 55 + -0.095009 0.126274 0.033823 0.178707 + 0.000038 0.000188 0.000021 0.000395 +significant elliptical 60 + -0.074176 0.104458 0.033514 0.146840 + 0.000036 0.000068 0.000023 0.000147 +significant elliptical 70 + -0.056322 0.109460 0.037122 0.153553 + 0.000015 0.000079 0.000022 0.000160 +significant elliptical 74 + -0.049055 0.109308 0.045872 0.151519 + 0.000022 0.000102 0.000021 0.000219 +significant elliptical 97 + -0.065350 0.112271 0.044407 0.156304 + 0.000026 0.000295 0.000028 0.000619 +significant elliptical 23 + -0.067418 0.125733 0.064109 0.172536 + 0.000035 0.000325 0.000013 0.000714 +significant elliptical 50 + -0.030036 0.114855 0.047891 0.159488 + 0.000113 0.000180 0.000127 0.000301 + +^ 6 +significant elliptical 79 + 0.558222 0.076427 0.054828 0.090347 + 0.000597 0.000045 0.000079 0.000087 +significant elliptical 34 + 0.501872 0.133686 0.091306 0.135354 + 0.000193 0.000020 0.000050 0.000041 +significant elliptical 39 + 0.506849 0.164867 0.121266 0.132542 + 0.000188 0.000116 0.000086 0.000071 +significant elliptical 108 + 0.472142 0.163577 0.119919 0.129286 + 0.000077 0.000097 0.000045 0.000079 +significant elliptical 94 + 0.430134 0.142877 0.114053 0.115289 + 0.000174 0.000085 0.000047 0.000079 +significant elliptical 40 + 0.367571 0.198585 0.139560 0.135962 + 0.000096 0.000018 0.000020 0.000018 + +v 1 +significant elliptical 630 + 0.292634 0.203086 0.148552 0.134961 + 0.000463 0.000705 0.000077 0.000328 + +w 1 +significant elliptical 634 + 0.272351 0.300050 0.154160 0.193189 + 0.000368 0.002315 0.000122 0.000537 + +x 1 +significant elliptical 634 + 0.241075 0.236781 0.172612 0.148433 + 0.000178 0.001371 0.000093 0.000463 + +y 1 +significant elliptical 525 + 0.209765 0.255096 0.210389 0.144287 + 0.000440 0.001435 0.000201 0.000460 + +z 1 +significant elliptical 616 + 0.237871 0.226094 0.169354 0.129572 + 0.000197 0.000977 0.000087 0.000235 diff --git a/tessdata/pffmtable b/tessdata/pffmtable new file mode 100755 index 0000000000..be615c8b15 --- /dev/null +++ b/tessdata/pffmtable @@ -0,0 +1,92 @@ +! 27 +# 44 +$ 45 +% 46 +& 46 +' 7 +( 28 +) 27 +* 14 ++ 26 +, 10 +- 9 +. 5 +/ 26 +0 53 +1 28 +2 50 +3 47 +4 34 +5 50 +6 46 +7 37 +8 50 +9 47 +: 10 +; 17 +< 29 += 29 +> 31 +? 32 +@ 54 +A 45 +B 52 +C 47 +D 57 +E 49 +F 41 +G 58 +H 56 +I 26 +J 33 +K 51 +L 34 +M 55 +N 53 +O 56 +P 44 +Q 60 +R 53 +S 49 +T 34 +U 55 +V 44 +W 49 +X 47 +Y 38 +Z 46 +[ 35 +\ 26 +] 35 +^ 18 +_ 17 +` 8 +a 39 +b 51 +c 36 +d 51 +e 35 +f 29 +g 56 +h 49 +i 24 +j 34 +k 45 +l 27 +m 59 +n 42 +o 43 +p 50 +q 49 +r 24 +s 35 +t 28 +u 43 +v 33 +w 40 +x 37 +y 41 +z 36 +{ 34 +} 34 +~ 18 diff --git a/tessdata/soptable.cls b/tessdata/soptable.cls new file mode 100755 index 0000000000..833f598e2c --- /dev/null +++ b/tessdata/soptable.cls @@ -0,0 +1,6544 @@ +!1 ! +B 0.000000 20 64.005249 57.521229 60.611259 118.432755 3.812411 33.310555 45.922043 ++ 1.611265 1.986372 11.780315 9.845653 5.785399 1.626407 9.161409 +B 0.000000 18 0.041902 72.882736 60.488358 116.439392 3.261841 31.351467 54.353531 ++ 2.829066 1.779686 4.010518 6.066471 1.268941 0.955935 12.218236 +B 0.000000 1 32.680893 63.734104 35.594570 112.730598 9.530541 30.306316 17.547375 ++ 1.353523 1.307659 7.895417 7.135591 2.537544 1.155637 8.823147 +A 0.000000 180 62.079082 66.383209 96.301903 ++ 4.767420 11.910351 7.809275 +A 0.000000 179 102.330330 119.694664 15.214146 ++ 34.684856 9.821125 6.328176 +L 1.000000 199 29.430170 70.497284 85.841728 116.536598 ++ 2.086412 2.554899 6.711692 15.364473 +L 0.000000 192 96.893311 58.511299 87.570068 111.719582 ++ 2.804138 2.345820 10.066628 18.790897 +L 0.000000 178 60.813358 65.373863 133.653061 102.132111 ++ 6.171493 2.291151 6.796940 16.818260 +L 0.000000 142 126.326439 63.296074 3.189127 94.442337 ++ 6.027892 3.945562 5.317891 13.685312 +L 0.000000 129 94.356041 56.746902 15.788860 95.896057 ++ 6.563445 4.073596 5.726952 16.635715 +L 0.000000 122 30.143476 70.368103 13.499565 93.054291 ++ 5.806498 4.840635 5.033850 13.784767 +L 0.000000 111 61.193565 64.764519 25.967413 97.698395 ++ 6.731588 4.131020 7.098120 15.924765 +L 0.000000 65 123.627510 64.022629 43.112522 84.461197 ++ 6.765898 5.389733 13.039365 15.538884 +L 0.000000 2 105.982399 55.597656 3.981614 72.735725 ++ 5.369670 3.294510 6.185365 14.291052 +L 0.000000 1 122.439980 62.764889 68.031075 70.823334 ++ 5.369670 3.294510 6.185365 14.291052 +S 0.000000 149 94.374069 49.661911 65.803268 72.434273 55.806671 67.238960 ++ 21.097900 21.207048 8.920137 8.324635 14.419332 6.523633 +S 0.000000 33 98.721558 0.506279 70.305038 72.446854 0.756544 121.370300 ++ 14.443996 0.319258 4.360436 3.223060 1.182804 7.701436 +S 0.000000 7 122.199402 0.968744 15.967284 81.553398 10.591908 23.478754 ++ 8.826340 0.861821 8.314072 5.696407 9.063377 6.463427 +S 0.000000 5 98.933891 0.655430 71.797798 73.858223 23.813831 92.240677 ++ 9.926503 0.321238 0.859499 5.266938 4.816579 6.544582 +S 0.000000 4 67.741585 0.578889 75.022469 64.750900 5.043105 81.109215 ++ 4.679764 0.257436 4.285591 0.915272 4.435626 8.844058 +S 0.000000 1 97.192062 27.315863 96.186096 73.626740 51.976124 89.135071 ++ 11.794901 4.593360 4.802827 4.685262 6.263472 7.215427 + +74 1 +3 98f8 +1 8af8 +4 9af8 +3 9ef8 +2 94f8 +2 91f8 +1 9cf8 +1 81f8 +2 9f78 +6 99f8 +2 9df8 +3 97f8 +2 96f8 +3 90f8 +3 9bf8 +3 8bf8 +1 93f8 +1 95f8 +1 86f8 +1 9db9 +2 83f8 +1 8ef8 +27 8ff8 +14 9ff8 +2 8f78 +12 87f8 +2 40ff8 +3 8df8 +1 40ef8 +2 9fb8 +1 97b8 +14 81e3 +1 87fc +1 80e3 +2 81a3 +12 10ff8 +1 816f8 +3 81ff8 +4 107f8 +1 126f8 +3 106f8 +2 10af8 +1 10bf8 +1 85f8 +1 8068 +1 20ff1 +1 87b8 +1 89f8 +1 a7f8 +4 8078 +3 8178 +2 8478 +1 88f8 +1 212f8 +1 8a78 +1 20178 +2 20078 +1 8878 +1 201f8 +1 82f8 +1 8cf8 +1 200f8 +1 101f8 +2 11ef8 +1 103f8 +4 100f8 +1 104f8 +1 8163 +1 10ef8 +1 400e8 +1 8678 +1 daf8 +1 41ff8 +1 100ff8 + +"1 "'` +B 0.000000 53 32.917751 66.893135 136.424866 104.747765 14.502829 35.261982 28.648586 ++ 2.026971 1.710088 4.620895 8.883481 10.482641 3.017868 5.813509 +B 0.000000 49 96.033394 64.949280 92.338272 82.948761 54.349091 35.888378 39.549973 ++ 1.194687 1.874189 4.708661 13.641004 27.168390 11.923002 10.968621 +B 0.000000 1 64.374168 59.960468 108.029213 108.838829 8.104903 28.665337 19.031677 ++ 1.610829 1.792139 4.279703 11.262242 17.951962 7.470435 8.391066 +C 0.000000 2 61.409584 95.827957 7.632182 ++ 2.000000 2.000000 4.000000 +C 0.000000 1 68.037643 112.339325 13.546643 ++ 2.000000 2.000000 4.000000 +A 0.000000 116 63.089207 82.851768 107.243385 ++ 8.142894 10.135328 11.562074 +A 0.000000 4 42.675938 108.137352 110.023766 ++ 1.092904 0.233276 1.731847 +L 0.000000 162 29.969873 73.596947 105.248466 109.739548 ++ 2.456082 4.562919 10.082978 11.632596 +L 0.000000 136 96.373451 56.374901 109.865898 104.738678 ++ 2.870304 3.928957 7.187507 14.587605 +L 0.000000 70 62.992245 65.369034 130.455597 93.288078 ++ 6.843008 2.463189 8.537724 19.246178 +L 0.000000 43 102.262703 63.954960 92.662773 87.398216 ++ 5.150534 6.681442 8.784020 17.696766 +L 0.000000 19 26.234577 63.073391 98.364716 81.468620 ++ 1.261471 1.207681 3.075357 5.945698 +L 0.000000 15 126.251526 64.976913 89.162689 107.552681 ++ 3.204662 1.066590 2.895852 18.090792 +L 0.000000 8 126.511879 65.243004 45.813858 73.548447 ++ 3.067617 1.424386 2.107601 3.418300 +S 0.000000 145 75.311569 60.252693 65.184814 66.081261 61.769352 64.841568 ++ 23.659084 15.606576 7.380055 8.272152 9.452324 9.066730 +S 0.000000 14 109.702713 1.810798 69.139412 77.717117 35.715065 85.079994 ++ 15.578835 2.910484 5.540183 8.195443 5.402968 12.464760 +S 0.000000 2 59.191170 43.175125 32.253544 57.296333 48.607910 39.530525 ++ 16.004377 6.874191 5.332610 6.969996 6.639952 9.380550 +S 0.000000 2 88.636627 77.899170 35.085110 69.907829 73.522491 38.634216 ++ 16.004377 6.874191 5.332610 6.969996 6.639952 9.380550 +S 0.000000 1 21.150835 0.497736 21.644159 53.344753 43.557396 11.072624 ++ 16.004377 6.874191 5.332610 6.969996 6.639952 9.380550 +S 0.000000 1 128.000000 104.640076 64.468620 88.290337 74.419304 56.773701 ++ 16.004377 6.874191 5.332610 6.969996 6.639952 9.380550 + +41 1 +38 41a0 +20 43a0 +1 40a0 +1 44a0 +3 42a0 +5 46a0 +7 66a0 +1 62a0 +2 51a0 +8 53a0 +3 81a0 +1 4420 +2 51a1 +3 51c1 +4 4383 +1 100a4 +10 4183 +1 40a3 +1 438b +1 43a3 +1 41a3 +1 43c2 +1 418b +1 401a0 +1 806a0 +1 100a0 +1 206a0 +2 87a0 +3 47a0 +1 85a0 +2 4120 +5 4583 +3 4083 +10 4d83 +3 4f83 +3 4983 +1 20983 +8 83a0 +1 4783 +2 4c83 +1 4193 + +#1 # +B 1.000000 200 71.780418 35.569164 25.199982 98.619873 16.422028 24.666708 53.385433 ++ 1.332086 5.887717 5.889737 7.602705 4.376776 4.337299 7.097179 +B 1.000000 200 96.013191 56.264626 14.567207 69.115692 65.855446 69.682861 66.196533 ++ 2.536978 4.595855 7.480374 3.311084 27.151493 31.930305 12.339417 +B 1.000000 200 124.097610 92.017067 62.197090 75.739311 35.302826 22.330013 53.798351 ++ 3.051237 3.547547 4.916439 5.491346 8.697236 13.356896 10.210320 +B 1.000000 200 48.448883 50.142044 104.675957 106.164467 12.126544 36.129093 57.959892 ++ 3.093846 2.710504 7.041606 4.671948 2.689128 4.583696 7.475208 +B 1.000000 200 60.235619 38.701756 65.129356 74.662582 37.231163 20.870979 55.688644 ++ 3.074290 3.953526 5.034546 5.584909 9.291034 11.178568 8.716244 +B 1.000000 200 113.087608 80.664711 22.130527 107.092339 11.317513 35.990520 56.016106 ++ 3.117528 2.600755 5.689172 3.234806 1.940226 2.590454 5.810111 +B 1.000000 200 32.513309 74.935883 111.955048 68.792107 66.981415 76.452736 64.452599 ++ 1.092054 3.390979 7.571812 3.473806 21.487085 28.210394 9.699892 +B 0.000000 199 6.916679 95.619423 101.699730 99.971710 15.287798 25.223421 50.652611 ++ 2.224856 5.392128 6.794118 8.895538 4.767903 4.167789 6.794169 +C 0.000000 193 65.553329 63.031071 19.434345 ++ 1.518856 5.060821 6.221398 +S 0.000000 197 65.503014 65.722191 73.537514 65.647865 65.249687 64.209984 ++ 3.898895 4.433608 6.260215 3.353769 4.428040 4.756278 +S 0.000000 3 64.202377 79.646606 94.168541 64.940369 68.038368 80.287842 ++ 4.540500 3.609126 2.858878 2.832558 1.571311 3.284168 + +4 1 +189 3ff +1 37f +3 5ff +7 2ff + +$1 $ +B 1.000000 400 63.757893 52.474613 48.275486 63.924374 35.620052 88.495590 87.255516 ++ 2.219936 4.282555 5.384981 6.722664 22.176849 22.304157 14.926564 +B 1.000000 400 1.415084 77.424759 86.307747 69.373657 30.100054 86.091515 78.988914 ++ 2.087108 5.300478 8.704967 11.018600 18.724031 25.634615 12.595261 +B 0.000000 334 111.848259 72.480453 -2.793824 109.816849 8.466534 32.518211 31.505753 ++ 2.661370 4.274125 4.406165 5.145746 3.421825 2.733102 6.145999 +B 0.000000 327 81.065109 52.040508 -1.640572 108.052460 9.637445 34.361176 31.275534 ++ 2.498991 4.169073 4.278265 5.586042 4.647019 5.424204 9.161527 +B 0.000000 171 18.975332 78.798500 127.152679 109.688889 8.693463 34.857944 24.075378 ++ 2.877942 4.935247 6.340425 7.486902 8.198008 7.337857 6.039721 +B 0.000000 170 45.745937 57.685108 127.893448 111.174980 7.748262 31.834290 25.554502 ++ 2.776468 4.834202 5.681841 6.965476 4.472526 5.249751 6.366097 +C 0.000000 319 72.194366 32.624142 22.274458 ++ 2.051180 3.458103 5.669805 +C 0.000000 160 58.319630 90.358994 17.887806 ++ 2.391423 3.113743 4.276769 +C 0.000000 158 59.305393 114.049187 16.937840 ++ 4.467888 3.971383 4.075617 +L 0.000000 183 53.000160 76.444527 68.518120 80.120384 ++ 2.940598 5.196254 6.948206 14.452828 +L 0.000000 140 117.529259 54.601276 60.076981 79.183418 ++ 3.865152 5.296862 7.631669 13.974800 +L 0.000000 74 54.211468 75.358780 131.081940 73.390182 ++ 2.949212 1.933429 8.916071 5.060909 +L 0.000000 42 76.318916 55.501476 130.279205 74.738655 ++ 2.238478 3.978876 8.665147 7.453161 +L 0.000000 28 117.026497 53.652988 0.359181 79.766960 ++ 2.061982 1.387179 3.935222 6.376854 +L 0.000000 24 13.309243 75.866409 0.782961 75.467133 ++ 1.974284 2.385188 4.751681 6.675047 +L 0.000000 13 66.152496 65.193222 31.508623 70.519150 ++ 5.051671 4.712892 3.648978 4.120340 +L 0.000000 6 121.398460 72.022453 88.314133 68.578773 ++ 0.748798 1.000938 3.514334 2.913505 +L 0.000000 2 4.941011 61.551262 105.120956 65.288330 ++ 2.728772 3.236452 6.001413 7.628430 +S 0.000000 396 66.272125 65.789886 38.040054 68.810516 64.488365 53.091976 ++ 6.015302 14.011927 13.351356 3.074974 6.479433 14.870383 +S 0.000000 4 61.934837 92.047562 95.819405 71.852386 67.840302 109.322723 ++ 1.216926 3.062648 1.083283 1.214267 0.627104 1.475752 + +148 1 +40 4063f +12 4863f +2 6063f +1 4163f +7 4023f +1 4003f +4 5063f +5 4061f +1 40e0f +1 4001f +1 4043f +1 4121f +1 5043f +1 4843f +1 40e2f +1 5141f +6 408ff +3 40aff +1 404ef +4 402df +4 400df +2 412df +5 400ef +12 400cf +2 40eff +9 406ff +2 410df +6 40aef +2 408f7 +1 404ff +1 410cf +4 40eef +3 406ef +6 400ff +5 402cf +3 402ef +1 40ae7 +2 408cf +4 402ff +2 416df +2 426ef +1 426cf +2 406df +6 404cf +2 416cf +1 412ff +1 40ecf +1 422ff +1 406fb +1 400d3 +1 400e7 +1 400d7 +2 404eb +1 404f7 +1 402e7 +1 410d3 +2 404e3 +1 400fb +1 448fb +1 402cb +1 410e3 +1 406e3 +1 404df +1 402f7 +1 444d3 +2 402fb +1 414cb +1 40ccb +1 404db +1 402c7 +1 420df +1 404d7 +1 404fb +1 402d3 +1 406cb +1 408e3 +1 424f7 +1 40cef +1 416c7 +1 40eeb +1 412cf +1 440cb +1 40ac7 +1 422c7 +3 404c7 +2 440c3 +1 420c7 +1 444c3 +1 416cb +1 456cb +1 442c7 +1 420cf +1 400cb +1 404cb +1 406cf +1 404c3 +12 4034f +4 40b4f +38 4014f +3 4014b +2 44143 +4 42347 +2 40143 +1 40343 +1 4074f +1 44b4b +2 40347 +1 4494b +2 42747 +1 44543 +2 40943 +3 42147 +1 4034b +1 42b47 +1 44b43 +1 42f47 +1 4054f +4 4016f +5 4096f +13 4094f +1 4086f +1 40b6f +5 4114f +8 4015f +2 4115b +3 8015f +4 4115f +1 44173 +3 4017f +1 4417b +1 40147 +1 4017b +1 4515b +1 8016f +2 41143 +2 43147 +1 42947 +1 4104f +1 4414b +3 44943 +1 4314f +1 42163 +1 45143 +1 4514b +1 44903 +1 42143 +2 43143 +3 40947 + +%1 % +B 0.000000 60 72.702492 51.724373 71.218567 66.211548 101.636101 0.466845 112.317917 ++ 0.718449 4.829939 11.862422 1.763211 13.053645 0.294665 18.232014 +B 0.000000 60 32.904747 70.762146 138.154938 107.974907 7.413080 33.734741 62.097881 ++ 0.877964 2.977327 2.295249 4.346498 1.601868 1.098072 9.312941 +B 0.000000 15 127.346756 86.669395 54.794147 85.870247 34.121941 43.451653 73.994476 ++ 0.685359 1.343028 4.394920 2.328104 3.838725 7.949286 4.201117 +B 0.000000 15 81.585716 52.267368 33.893913 83.892731 42.533215 35.406456 58.381401 ++ 0.492817 1.270299 4.289475 3.816056 8.415298 7.273956 6.336056 +B 0.000000 10 18.429241 75.289444 92.232033 81.262405 51.694641 42.621468 60.199478 ++ 1.173511 2.323315 5.229602 2.912778 9.938715 12.706806 7.094656 +B 0.000000 9 64.353043 41.949039 69.836815 83.795265 38.311443 34.717419 68.693176 ++ 0.267320 1.893033 4.321119 2.581785 7.220465 8.370083 5.658684 +B 0.000000 2 117.529999 76.662689 84.088394 105.143715 2.771288 30.516354 43.302032 ++ 0.682613 2.260382 6.083143 2.736723 7.160171 5.855890 7.808948 +B 0.000000 2 7.364782 88.888718 80.687195 93.458115 27.219799 15.756346 98.895462 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 80.814133 27.007004 61.409775 108.697258 8.017445 36.852703 14.815441 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 46.480942 78.591820 57.101963 107.485825 6.065760 29.517086 17.334299 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 71.556648 39.788380 47.366936 95.651222 26.321863 20.490765 106.206688 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 32.181034 75.095444 53.338779 96.844521 21.516352 32.316708 10.818478 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 38.965034 76.384880 86.618095 102.984917 6.302010 36.736439 26.139830 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 96.489365 65.346039 28.281099 68.387222 71.945526 90.329887 40.896194 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 107.874611 73.372108 64.073677 106.827408 2.606960 29.625576 27.243799 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 96.752007 63.538063 15.716076 85.359848 38.157230 37.430893 84.093941 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 62.168449 56.435844 75.182060 76.162506 47.485115 0.603288 44.823120 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 32.959705 67.172867 102.884949 79.488098 51.964588 61.309872 45.320354 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +C 0.000000 196 87.757469 33.484589 36.486294 ++ 9.026154 7.206942 9.776224 +C 0.000000 182 44.059406 99.273140 36.455677 ++ 6.631586 5.675539 10.473967 +A 0.000000 117 39.988430 42.145397 66.998108 ++ 10.192586 8.032516 5.731821 +A 0.000000 2 109.738327 121.881004 34.315750 ++ 10.192586 6.693762 5.731821 +A 0.000000 1 35.431862 121.023369 100.135658 ++ 10.192586 6.693762 5.731821 +L 0.000000 182 20.234632 68.605057 65.265778 123.767220 ++ 4.388373 3.895850 6.239277 21.479467 +L 0.000000 161 86.213554 57.726051 65.248619 122.635735 ++ 3.549580 4.182896 7.194473 11.155860 +L 0.000000 108 76.429947 77.888443 60.170063 85.107697 ++ 12.706413 13.387260 16.813847 20.054466 +L 0.000000 86 122.883698 63.938198 4.389029 108.263260 ++ 6.426983 3.919092 3.627388 18.298521 +L 0.000000 83 56.729519 64.501167 129.201294 111.938850 ++ 5.960134 4.010108 6.789066 15.938410 +L 0.000000 65 30.115345 111.594490 38.178707 77.612328 ++ 3.531445 10.477723 9.383040 10.528790 +L 0.000000 43 125.132202 88.566246 4.280533 80.203934 ++ 5.303766 9.253276 4.086534 20.306862 +L 0.000000 35 125.082077 41.462341 67.607437 76.725449 ++ 6.497273 4.524006 9.998478 10.719562 +L 0.000000 28 62.301895 45.836197 124.638161 81.047539 ++ 4.740659 6.378730 6.332509 15.927987 +L 0.000000 26 30.315647 60.320133 94.605827 74.877930 ++ 4.094895 7.405896 5.537346 9.219893 +L 0.000000 23 95.734032 24.861710 96.412453 74.433220 ++ 3.007564 4.587984 5.816247 7.345078 +L 0.000000 22 92.690964 73.417374 36.689964 80.910927 ++ 7.952679 9.317863 6.236247 16.857399 +L 0.000000 6 73.741623 58.799637 70.449753 116.916435 ++ 1.268464 5.042543 4.938092 14.871120 +L 0.000000 2 25.375757 81.037315 91.776314 76.199600 ++ 4.929045 6.006733 6.986472 12.586694 +L 0.000000 1 7.316662 97.741066 25.011549 81.612129 ++ 4.929045 6.006733 6.986472 12.586694 +L 0.000000 1 88.546700 48.906712 35.000549 77.729248 ++ 4.929045 6.006733 6.986472 12.586694 +S 0.000000 111 65.962334 40.677032 77.330132 64.679764 48.131207 75.048538 ++ 11.875165 32.061302 10.027500 3.720654 22.930979 17.972803 +S 0.000000 59 128.000000 8.066235 69.962517 81.812561 58.797569 84.243614 ++ 0.001000 9.442021 6.254001 3.353652 3.404517 4.150948 +S 0.000000 8 52.404823 125.686325 35.861328 57.546665 75.666473 50.091816 ++ 10.056108 6.121408 3.724666 2.705703 3.258295 3.452263 +S 0.000000 7 66.871925 59.215614 24.827606 64.603767 63.085159 0.907635 ++ 2.695360 3.766287 3.780344 1.565666 2.280106 0.806101 +S 0.000000 6 68.013634 69.831207 128.000000 64.665993 65.095947 128.000000 ++ 7.134042 16.306585 0.001000 1.603715 5.286144 0.001000 +S 0.000000 2 86.573776 18.746975 39.168121 72.139366 54.487000 55.713314 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 +S 0.000000 2 0.397388 105.180603 49.031040 55.588631 68.235924 60.861103 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 +S 0.000000 1 65.008240 0.214330 28.608702 67.438713 57.457230 37.967861 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 +S 0.000000 1 128.000000 20.772209 47.526871 67.017273 56.456413 61.294243 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 +S 0.000000 1 128.000000 3.836183 0.203989 80.281288 16.380184 22.980471 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 +S 0.000000 1 31.726650 128.000000 94.831177 54.212475 95.668846 122.816071 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 + +100 2 +1 469c0000 80 +7 29c0000 80 +3 29c0000 800 +1 129c1800 88 +1 28c0030 1004 +1 28d0010 1004 +1 429c0000 88 +4 20c000c 200 +1 420c000c 201 +1 a9c0000 81 +1 559c0000 80 +2 29c0000 88 +1 29c4000 88 +2 c003c 400 +1 90c000c 202 +5 20c003c 400 +1 a0c000c 200 +1 28c0030 4000 +1 420c000c 200 +1 29c0000 8c +1 29c0000 81 +1 429c0000 80 +14 d9c0000 80 +1 d9c0000 81 +1 119c0000 802 +1 1940000 80 +1 2d9c0000 80 +4 9940000 80 +1 5940000 80 +1 259c0000 80 +1 11940000 800 +1 1f9c0000 81 +1 6f9c0000 82 +3 4d9c0000 80 +1 859c0000 82 +6 1d9c0000 80 +3 d940000 80 +1 459c0000 80 +3 85940000 80 +1 bbb80000 a6 +1 df9c0200 85 +1 f59c0000 83 +2 59c0000 80 +2 ff9c0000 80 +1 8f9c0000 81 +1 ff9c0000 83 +1 ad9c0000 80 +1 dd9c0000 83 +1 ef9c0000 80 +1 2f9c0000 82 +2 2f9c0000 83 +1 cf9c0000 84 +3 f9c0000 80 +1 1f9c0000 84 +1 5d9c0000 80 +2 f940000 80 +1 a99c0000 82 +2 99c0000 80 +1 6d9c0000 82 +1 1940000 800 +1 ad9c0000 83 +1 1f9c0000 80 +1 6f9c0000 85 +1 3f9c0000 81 +1 8db80000 82 +1 cfd40000 87 +1 cf9c0000 80 +1 3f9c0000 83 +1 9f9c0000 84 +1 f79c0000 85 +1 4f9c0000 80 +1 c10c8080 2013 +1 5d8c0040 82 +1 7d9c0000 81 +1 ef9c0000 84 +1 228e0400 8044 +1 7f9c0000 81 +1 10c2080 2011 +1 f79c0000 20007 +1 e99c0000 80 +1 2d9c0000 83 +2 8d9c0000 80 +1 d9c0000 82 +1 6d9c0000 80 +1 d9c0100 80 +1 4d9c0000 82 +1 79c0000 80 +10 118c0003 100 +7 338c0003 100 +7 18c0003 100 +13 138c0003 100 +2 138c0003 104 +1 218c0003 10000 +9 38c0003 100 +1 1880003 100 +1 38c0043 100 +3 238c0003 104 +1 318c0003 100 +3 338c0003 104 +2 38c0003 101 + +&1 & +B 1.000000 400 1.769900 95.378227 37.913578 99.394669 15.483581 23.088934 66.032753 ++ 2.179321 4.034113 6.395760 11.999109 7.517179 11.208960 21.944414 +B 1.000000 400 57.928349 39.938034 81.293625 107.386726 11.648932 32.817337 54.824482 ++ 3.105267 4.943439 8.339809 5.293695 4.624905 3.907837 12.078005 +B 0.000000 399 97.054329 75.280838 3.959608 108.362785 9.070305 32.798832 45.377728 ++ 0.976586 4.337253 3.011638 4.847668 2.844659 3.038387 11.538806 +B 0.000000 383 10.316792 77.891098 76.643471 62.211922 41.388752 11.371058 75.004021 ++ 3.908411 3.942716 5.053528 10.974621 23.871981 13.649494 15.038361 +B 0.000000 17 16.859518 91.823341 107.455650 101.808769 16.310375 33.526371 62.511761 ++ 0.662504 1.389329 2.411850 3.892734 3.167619 2.604382 5.138044 +C 1.000000 400 54.172848 38.117916 40.929733 ++ 4.406622 3.418796 10.910159 +C 0.000000 320 63.673759 110.392273 26.816572 ++ 4.601273 5.546567 4.523160 +C 0.000000 17 75.993713 75.387497 38.858311 ++ 1.256557 2.698415 1.385175 +L 0.000000 20 3.037512 60.160683 4.730303 70.116608 ++ 0.835668 5.264192 2.563785 5.864570 +L 0.000000 20 42.288597 65.761841 72.429314 67.937424 ++ 1.018120 1.089069 1.925194 2.094218 +L 0.000000 15 21.444195 96.072189 62.283264 66.809654 ++ 0.332007 2.554489 2.341492 0.746965 +L 0.000000 3 60.686195 67.645905 113.595055 68.236481 ++ 0.083948 1.119920 2.549423 2.176132 +L 0.000000 2 78.250885 38.496170 76.896629 68.050964 ++ 0.567436 2.506917 2.344974 2.720471 +S 0.000000 223 20.748497 31.176178 54.890240 65.923859 59.787624 51.210503 ++ 10.120691 11.324469 6.165760 3.107170 4.377853 5.101822 +S 0.000000 135 22.990271 39.485134 79.867393 59.407043 61.065636 68.786835 ++ 6.095737 17.737057 4.251659 4.909535 2.060074 9.279860 +S 0.000000 40 1.764504 59.089230 98.021378 58.959236 57.680214 98.529312 ++ 2.244428 3.604089 3.526631 1.880227 1.648564 4.344600 +S 0.000000 2 25.291315 50.749039 22.820040 67.028000 72.188347 23.361437 ++ 6.153618 10.888538 4.648016 3.298977 2.695497 6.242094 + +18 1 +49 202f +19 222f +1 232f +6 212f +3 282f +1 1002f +1 1012f +145 206f +106 406f +1 416b +2 416f +8 446f +8 816f +30 806f +2 906f +11 40f7 +6 44f7 +1 456f + +'1 '" +B 0.000000 135 65.247971 58.984913 106.501114 106.699768 11.713119 33.368660 36.245907 ++ 3.235197 1.618732 5.036223 6.697288 5.193398 6.732397 8.668591 +A 0.000000 77 52.479107 81.915787 102.097420 ++ 8.490204 7.126872 15.403316 +L 0.000000 186 26.125938 71.638252 104.521606 92.364746 ++ 4.765816 2.212979 14.773415 19.723982 +L 0.000000 174 105.846901 59.714100 104.787773 91.608986 ++ 13.339334 4.265795 26.808899 18.622189 +L 0.000000 132 60.825531 64.811050 131.604050 89.091187 ++ 6.399921 2.638608 9.153120 17.590307 +L 0.000000 16 88.976730 56.398670 96.590385 118.472427 ++ 1.185636 2.028556 13.337788 6.296843 +L 0.000000 9 8.066439 65.458908 88.618225 78.221825 ++ 6.516279 1.751802 6.452028 17.894356 +L 0.000000 6 103.045128 58.783184 116.790573 83.058632 ++ 4.596146 1.275193 3.645932 8.053367 +L 0.000000 5 32.073074 74.856552 117.865089 76.049187 ++ 1.388092 1.327382 1.408385 8.323574 +S 1.000000 204 94.786659 60.010098 57.117470 68.364891 61.000404 62.124905 ++ 18.324696 29.844168 25.286516 5.546808 22.398418 37.328625 + +23 1 +34 21e +16 23e +13 20e +1 21a +15 215 +2 21f +44 21d +2 31d +6 25d +7 205 +3 219 +6 29d +2 217 +2 20a +2 207 +1 25e +2 20f +2 24e +29 20d +9 209 +3 201 +2 30d +1 305 + +(1 (` +B 1.000000 207 127.661018 72.829208 51.959213 112.804817 6.130986 32.442429 121.919624 ++ 1.610764 2.977527 3.592935 3.831924 1.156854 0.663137 8.824937 +B 0.000000 2 48.280495 78.062439 140.000000 106.165024 11.284923 27.318691 12.666225 ++ 1.610764 2.977527 3.592935 3.831924 1.156854 0.460512 8.824937 +L 0.000000 26 23.805696 65.150475 95.034645 71.619804 ++ 0.898691 2.665612 4.943768 4.240093 +L 0.000000 15 33.070206 67.100319 50.715790 70.187309 ++ 2.722514 1.368528 16.531031 3.323933 +L 0.000000 12 78.635033 68.227249 127.225632 87.563545 ++ 4.818888 3.795030 14.196296 16.260994 +L 0.000000 10 113.779884 67.448532 -19.922207 84.292877 ++ 1.479203 3.359662 6.301350 14.948366 +S 0.000000 167 62.085178 21.178179 65.195663 64.606056 53.942150 65.352150 ++ 10.308704 21.997496 8.283433 5.463855 4.977063 16.776699 +S 0.000000 40 78.962646 0.464763 65.510384 67.336929 14.447095 72.537300 ++ 8.494372 0.304111 9.198198 3.921777 8.019630 31.767397 + +14 1 +134 41 +9 51 +8 61 +12 49 +22 85 +14 81 +1 95 +1 8d +1 45 +1 71 +1 59 +1 69 +1 87 +1 83 + +)1 ) +B 1.000000 207 63.762260 57.604500 52.623043 112.666443 6.183340 32.486603 120.901871 ++ 1.612627 3.243293 4.180657 4.219477 1.183234 0.811905 8.802439 +L 0.000000 23 98.604706 62.478001 68.761559 75.091301 ++ 3.115773 2.673977 18.056223 6.013084 +L 0.000000 23 87.651207 64.936249 10.262928 71.922371 ++ 0.536370 1.189086 2.808139 3.890592 +L 0.000000 21 14.373419 63.898582 -16.893942 84.694557 ++ 3.784270 5.341216 9.897423 15.356453 +L 0.000000 15 51.706387 61.206699 129.819855 87.813927 ++ 2.356802 2.315979 7.231856 15.067945 +S 1.000000 207 65.491631 108.608017 64.879562 64.253960 80.691277 65.176537 ++ 13.865183 21.006952 7.833254 4.324200 20.876392 24.352365 + +9 1 +137 21 +16 23 +10 31 +12 29 +20 25 +3 2d +4 2b +2 39 +3 33 + +*1 * +B 0.000000 197 67.387177 45.696438 88.635506 97.004318 20.549053 33.132118 36.271816 ++ 2.722196 2.931146 15.932130 7.847195 11.146944 13.468978 6.325923 +B 0.000000 184 44.280064 54.656631 119.848877 98.818619 18.155893 31.695602 36.463184 ++ 2.653637 2.034827 11.633392 13.451281 7.370694 8.055589 11.522698 +B 0.000000 173 125.379883 84.124199 91.289009 97.210594 20.685204 34.977505 35.632927 ++ 2.863267 2.588150 13.114137 10.616257 12.417014 11.713616 6.057251 +B 0.000000 166 19.968300 76.478630 116.541260 96.327660 21.266388 33.956692 33.908615 ++ 1.957619 1.972492 10.958135 9.039450 10.408520 14.143635 7.068118 +B 0.000000 120 96.028809 64.777550 56.809639 97.577972 19.505840 34.199673 41.028206 ++ 1.023405 1.441266 13.605963 6.438075 5.832481 4.554849 5.494289 +B 0.000000 60 107.427208 75.389816 73.948494 95.294060 24.113192 31.709957 35.988144 ++ 1.519182 1.322400 7.171587 9.113683 14.153857 4.916488 4.654840 +B 0.000000 60 85.823761 55.067036 76.275742 85.604294 34.662750 37.810883 35.701252 ++ 1.437839 2.220204 6.302518 10.352435 24.151270 9.608008 7.988138 +B 0.000000 24 124.911240 87.507545 59.451660 97.861870 18.878767 37.519672 34.618595 ++ 1.066783 1.334939 1.759257 7.002640 5.723701 7.855723 8.210592 +B 0.000000 3 22.766794 74.316971 122.632271 65.351959 107.274185 73.769585 34.252087 ++ 0.136994 1.063279 1.758473 3.958699 14.656945 4.984944 5.457543 +B 0.000000 3 22.687609 73.639328 119.621468 67.043289 92.879005 23.810131 30.672483 ++ 0.604961 0.628315 0.339778 1.279761 14.641242 16.708050 4.241342 +C 0.000000 1 56.873287 114.662773 6.558966 ++ 2.000000 2.000000 4.000000 +C 0.000000 1 73.947166 74.505127 7.465426 ++ 2.000000 2.000000 4.000000 +A 0.000000 8 124.073715 121.345634 108.606827 ++ 17.813745 3.605340 3.830722 +L 0.000000 18 56.167110 74.523872 133.978668 81.873886 ++ 2.344275 1.851446 5.209478 3.602555 +L 0.000000 15 119.910522 56.345692 80.701569 80.598633 ++ 1.188395 1.929232 6.981928 4.971448 +L 0.000000 7 10.365947 74.861862 74.924606 76.484528 ++ 1.435173 1.735930 10.627545 4.782844 +L 0.000000 5 73.074036 56.075787 134.988297 73.706963 ++ 2.670759 1.247813 1.901062 5.175903 +L 0.000000 3 31.420795 85.954109 104.765091 78.141846 ++ 4.081337 1.993535 9.827012 6.905942 +L 0.000000 2 102.733841 48.978134 88.633163 94.990997 ++ 2.343988 1.751591 6.909405 5.087739 +L 0.000000 1 94.052376 46.550930 116.915222 75.591461 ++ 2.343988 1.751591 6.909405 5.087739 +S 1.000000 200 63.138992 65.161842 63.020428 56.866207 63.800228 63.367786 ++ 10.719078 12.742775 3.768673 10.800917 3.156570 5.393810 + +35 1 +90 10001f +24 10009b +1 12001b +2 102017 +1 14001e +1 142016 +1 100017 +46 10006f +1 10500d +2 10804f +1 100165 +1 102067 +2 100067 +3 100267 +1 10ac47 +1 100167 +1 110165 +1 10402f +2 106005 +1 106025 +1 1a7002 +1 102007 +1 10b007 +1 108007 +1 114005 +1 125003 +2 107005 +2 115005 +1 104005 +1 102065 +1 106007 +1 10a005 +1 110025 +1 106027 +1 10a045 + ++1 + +B 1.000000 200 79.420723 50.361393 38.278347 101.084679 16.603939 31.154814 66.691521 ++ 0.825323 2.120786 5.642311 3.417524 1.945465 2.251870 7.405800 +B 1.000000 200 49.312176 52.702477 83.251358 100.502892 17.286957 34.134171 66.358788 ++ 0.731159 1.975188 7.749459 2.872129 1.788612 2.906186 5.585153 +B 1.000000 200 15.295288 80.416557 79.486877 100.869438 16.713589 30.696753 67.042282 ++ 1.164947 2.165697 7.450608 3.856524 3.784103 2.375234 5.485160 +B 1.000000 200 113.257996 77.840324 34.014549 101.093880 16.784124 33.721828 66.197861 ++ 0.832649 2.033843 6.479857 3.167571 2.319700 2.015274 5.518560 +L 0.000000 1 43.820614 64.138008 87.176353 85.582741 ++ 3.000000 8.000000 8.000000 32.000000 +S 0.000000 198 63.283543 65.529419 55.045242 64.863281 64.558685 73.189270 ++ 24.045494 26.776758 5.377308 6.580789 4.425867 6.560078 +S 0.000000 2 61.184067 76.152260 35.144226 61.289265 62.826065 19.254808 ++ 11.596011 12.913173 5.377308 3.808326 3.073519 5.466732 + +3 1 +198 2f +1 5f +1 4f + +,1 , +B 0.000000 142 64.537949 59.096947 -3.658900 105.840141 12.562458 33.706047 37.409039 ++ 2.714293 1.607178 4.293151 13.683870 8.709389 7.072926 7.817279 +B 0.000000 1 113.992928 69.962807 0.768940 108.756432 3.814529 34.098042 22.305206 ++ 2.714293 1.607178 4.293151 7.918906 6.048186 4.911754 7.817279 +A 0.000000 125 49.407085 74.164825 5.141765 ++ 6.535633 11.095516 3.179369 +L 0.000000 228 25.412725 70.749672 0.554370 99.435364 ++ 4.417593 2.408163 6.149816 21.227579 +L 0.000000 225 96.530083 59.102531 3.629136 101.537605 ++ 9.205162 2.401191 8.843036 20.993820 +L 0.000000 188 62.123108 64.402573 28.505396 95.491051 ++ 6.796550 2.383424 6.644981 20.741386 +L 0.000000 59 5.194554 65.449379 -22.086563 85.784683 ++ 8.663555 2.083616 3.844467 18.441389 +L 0.000000 8 113.863861 60.599754 4.341672 91.890556 ++ 6.879754 1.184599 3.772843 11.564111 +S 1.000000 259 93.803520 52.005608 62.844429 69.064636 56.915226 69.157455 ++ 16.745472 31.561787 29.761110 6.447505 25.979591 31.268833 + +28 1 +75 13c +26 17c +9 11c +40 139 +3 13d +21 119 +1 149 +12 129 +8 109 +11 179 +3 12d +5 131 +2 169 +6 15c +2 11d +3 1b1 +9 111 +1 191 +2 1b9 +3 141 +3 151 +3 101 +2 121 +2 171 +4 159 +1 1f9 +1 199 +1 13e + +-1 - +B 0.000000 1 96.400230 64.115753 43.774364 121.437874 3.582798 31.939747 30.086756 ++ 2.000000 4.000000 4.000000 20.000000 20.000000 20.000000 8.000000 +A 0.000000 202 0.125693 80.991966 53.263374 ++ 3.141706 13.854100 4.321684 +L 0.000000 202 63.917660 65.627724 63.863136 107.457657 ++ 1.570510 4.341974 5.715289 11.228134 +L 0.000000 200 127.967125 65.172356 42.768833 108.756378 ++ 1.884227 2.644419 4.181104 14.002970 +L 0.000000 99 32.771687 84.051315 52.798584 90.063759 ++ 6.483339 5.640245 4.458272 14.570005 +L 0.000000 98 94.823624 46.377815 54.377426 92.719589 ++ 7.114461 6.087997 5.021265 16.996984 +S 0.000000 191 63.226185 64.855141 59.187042 63.489136 64.750725 58.063244 ++ 16.983227 11.303350 11.813718 11.377839 4.777973 13.292861 +S 0.000000 4 3.314805 64.506378 64.428581 11.091066 64.579689 64.367020 ++ 2.821479 0.270443 0.192021 10.767456 0.220723 0.215950 +S 0.000000 3 11.065296 38.296429 25.602531 40.570492 57.727207 32.493282 ++ 2.497861 0.351785 0.952618 0.307171 0.861278 0.234211 +S 0.000000 2 0.703657 50.752747 41.357773 40.505859 67.343979 48.723228 ++ 6.490676 2.823925 3.663135 6.852053 1.466679 3.842515 +S 0.000000 1 108.527481 27.510113 93.902878 88.461113 49.896519 96.292877 ++ 6.490676 2.823925 3.663135 6.852053 1.466679 3.842515 +S 0.000000 1 114.546806 86.076355 18.997795 88.990921 68.418282 24.563784 ++ 6.490676 2.823925 3.663135 6.852053 1.466679 3.842515 +S 0.000000 1 63.028595 67.593918 0.223784 64.690948 67.333549 0.588501 ++ 6.490676 2.823925 3.663135 6.852053 1.466679 3.842515 + +15 1 +69 4e +70 7e +24 6e +23 5e +3 10e +1 41e +2 be +1 56 +2 46 +2 22e +1 4d +2 8e +1 81e +1 5a +1 100e + +.1 . +A 1.000000 248 14.898916 121.463341 15.306340 ++ 34.718567 7.221146 3.595671 +L 0.000000 122 63.856510 65.574883 27.846304 89.175240 ++ 6.469222 2.020466 5.998548 16.509554 +L 0.000000 120 94.255737 57.413151 16.607862 87.967140 ++ 7.439978 2.162988 5.225257 14.483957 +L 0.000000 118 30.551205 73.188019 14.042198 90.145920 ++ 4.649730 2.463299 4.820208 16.458424 +L 0.000000 98 127.327705 65.309685 2.721646 86.609535 ++ 4.592943 2.814596 4.322944 16.416399 +L 0.000000 3 46.655781 71.624283 25.698103 92.105896 ++ 2.217208 2.826505 4.369421 1.406807 +S 0.000000 231 62.761314 64.775948 62.404198 63.366348 64.680511 61.270115 ++ 18.909386 15.890218 8.895386 9.908590 10.759688 11.296729 +S 0.000000 9 58.862164 67.858154 95.861267 61.442425 64.476006 94.572762 ++ 9.943202 8.888189 4.804637 4.711574 7.314241 2.880430 +S 0.000000 5 24.603836 63.491150 66.063332 35.147682 64.356163 66.729347 ++ 10.145714 2.388282 3.032114 7.478976 0.156762 4.427326 +S 0.000000 3 71.147369 14.400313 69.013145 68.612961 42.824783 73.044594 ++ 17.937428 9.582438 2.986989 2.782108 6.545814 6.134344 + +29 1 +3 53 +5 57 +17 4b +46 41 +13 51 +23 43 +8 49 +9 55 +10 5d +42 5f +1 75 +7 4f +4 5b +12 45 +8 47 +7 59 +14 4d +3 85 +2 83 +1 8b +2 20f +1 81 +1 89 +4 10f +1 11f +1 71 +1 213 +1 77 +1 87 + +/1 / +A 1.000000 200 46.726498 36.555374 50.615284 ++ 3.643897 6.017420 6.606476 +L 1.000000 200 87.524010 60.023106 53.705910 123.758850 ++ 1.843281 1.837095 21.668907 25.640997 +L 1.000000 200 23.609364 69.914833 48.368210 124.081551 ++ 1.819703 1.904038 9.955535 8.943349 +L 0.000000 193 120.575371 64.500977 -35.775707 118.323135 ++ 7.415007 1.720142 4.060833 24.168186 +L 0.000000 189 56.999939 65.505699 135.239639 115.056000 ++ 6.290235 2.497538 8.493941 23.560547 +S 0.000000 189 61.996330 56.567226 74.258179 63.642582 57.850513 71.056778 ++ 12.666356 31.963678 9.305552 3.134483 27.084574 9.304347 +S 0.000000 5 64.687454 128.000000 69.557396 62.322525 115.519501 65.994812 ++ 4.746367 0.001000 3.965414 2.436738 6.064809 2.662883 +S 0.000000 3 68.874763 74.018082 128.000000 63.818039 66.322334 128.000000 ++ 2.075386 15.279862 0.001000 1.330986 7.323642 0.001000 +S 0.000000 2 90.852852 1.206486 91.200638 71.112251 30.973810 106.534821 ++ 5.792350 13.972421 3.907014 2.300736 10.732393 3.041745 +S 0.000000 1 98.342796 0.920138 64.596664 74.520119 37.497040 87.506798 ++ 5.792350 13.972421 3.907014 2.300736 10.732393 3.041745 + +8 1 +181 3f +6 2f +3 5f +3 87 +1 21f +2 117 +2 37 +2 4f + +01 0o +C 1.000000 204 65.443657 64.911972 71.789330 ++ 1.096369 5.245805 9.905252 +L 0.000000 20 30.490431 91.634338 62.440331 69.652344 ++ 3.185317 2.726466 10.379773 6.924863 +L 0.000000 18 92.943909 39.309948 77.540466 67.992142 ++ 4.082936 2.483702 13.658937 3.445555 +L 0.000000 10 127.274796 63.865803 3.404732 70.951233 ++ 5.551590 4.390501 4.133164 4.823240 +L 0.000000 4 62.942997 66.577850 125.731682 66.773018 ++ 2.243795 2.926396 7.170950 1.378849 +S 0.000000 203 64.345139 64.574554 64.020416 64.363922 64.732986 62.440926 ++ 3.118469 6.252738 3.150025 2.104194 3.647523 3.640234 +S 0.000000 1 57.540237 71.876587 75.957611 62.131870 68.675461 74.406708 ++ 2.598724 3.015401 2.187517 1.753495 2.110835 2.772265 + +10 1 +160 21 +7 29 +15 23 +2 31 +1 45 +11 25 +1 39 +2 2d +4 27 +1 33 + +11 1l +B 1.000000 204 66.009773 54.850014 62.488228 87.045807 9.637583 27.073484 97.987251 ++ 3.460301 4.788383 4.892464 15.622623 2.704778 4.435328 19.271038 +B 0.000000 123 3.684168 79.359367 53.802174 105.798111 7.401834 27.509636 86.313629 ++ 3.036848 1.807917 5.475383 7.193706 2.931496 2.614546 12.829051 +B 0.000000 48 44.364120 62.451279 111.403694 111.743088 8.449205 33.429287 29.073931 ++ 1.440689 7.471102 5.909161 5.779646 1.917046 4.484986 7.850305 +B 0.000000 2 96.157211 67.321114 2.168291 113.146881 3.113133 33.476044 37.309235 ++ 2.645946 3.199589 4.539704 8.320694 2.517774 3.215117 11.759915 +L 0.000000 201 31.679005 76.712654 70.626640 114.310295 ++ 2.098028 3.443281 11.537759 18.029325 +L 0.000000 198 95.860733 61.233936 56.999432 90.869835 ++ 2.163868 4.133218 12.789112 12.205950 +L 0.000000 124 0.748730 66.422340 2.555645 112.395683 ++ 1.947150 3.117495 2.102409 21.459122 +L 0.000000 111 72.457870 61.249180 127.508110 94.406784 ++ 2.542600 3.245677 9.448014 19.455441 +L 0.000000 14 119.812012 59.026188 89.208794 83.693092 ++ 1.879756 0.307067 3.373134 2.705875 +S 0.000000 97 51.275787 60.944565 53.611416 56.034935 59.219772 51.701962 ++ 9.063824 24.109196 8.927049 2.997615 7.581006 12.949498 +S 0.000000 59 87.104431 128.000000 33.753597 65.703407 82.515755 9.393921 ++ 6.045363 0.001000 5.146061 1.896561 4.561212 10.770341 +S 0.000000 26 56.225433 127.885826 58.787403 59.378189 80.154839 65.735199 ++ 6.106666 0.396566 7.501747 2.557824 6.610920 14.362458 +S 0.000000 12 110.816612 127.256470 19.429132 68.948471 45.734917 3.372754 ++ 7.398993 1.690261 4.739276 2.301270 10.038724 4.031080 +S 0.000000 10 107.490334 98.710014 22.722826 68.985306 43.584049 18.059902 ++ 10.303163 11.539957 4.951231 2.921867 5.626542 8.334496 + +22 1 +45 273 +1 267 +1 263 +3 253 +41 2f3 +16 4b1 +6 5b1 +28 435 +7 535 +1 475 +3 1035 +3 2035 +5 20b1 +7 10b1 +2 10b5 +2 20b5 +1 5b5 +3 2d3 +26 8f3 +1 27b +1 2fb +1 2e3 + +21 2 +B 1.000000 201 0.366996 77.691170 40.121925 78.125465 41.895142 1.962537 86.066101 ++ 1.609591 3.455543 7.986717 2.802062 9.878490 5.180982 15.882639 +B 1.000000 201 63.351463 58.068138 75.243584 72.907227 31.193167 1.788466 116.267548 ++ 1.675175 5.534683 6.399627 7.512290 10.163117 7.289217 15.731024 +B 0.000000 1 96.571861 63.143925 0.784300 121.796661 3.031327 32.189182 33.538277 ++ 1.386453 3.361574 6.215451 4.531152 7.939918 3.263638 14.483278 +L 1.000000 201 0.033414 64.298096 2.170575 110.445908 ++ 0.915024 2.694438 1.616340 14.106071 +L 0.000000 157 79.862167 59.713055 52.801388 89.708076 ++ 2.831678 4.790684 9.131303 16.046009 +L 0.000000 129 15.761054 73.888741 49.883163 82.546570 ++ 2.509003 3.422866 8.588465 13.324942 +L 0.000000 119 65.141663 75.633385 23.101381 80.100273 ++ 3.427530 1.900157 7.741635 7.854553 +L 0.000000 24 6.430458 55.141293 104.906288 72.673958 ++ 1.572124 1.564250 12.709573 3.796712 +L 0.000000 14 62.263985 66.937370 121.063400 71.340111 ++ 5.705109 5.646492 8.548909 3.981741 +S 1.000000 201 57.310905 81.769020 88.347008 58.660851 71.307922 82.689331 ++ 5.887548 16.636705 10.430968 3.889091 7.172461 14.213943 + +23 1 +27 23b +13 22b +21 21b +4 29b +5 20b +1 3bb +1 23f +2 33b +5 2bb +2 2ab +47 27b +6 2fb +18 26b +33 25b +1 35b +2 24b +3 37b +3 36b +1 31b +1 2cb +2 3db +2 2db +1 3fb + +31 3 +B 1.000000 204 62.733475 65.538322 93.460243 78.923454 24.695372 19.982420 67.667450 ++ 1.640888 3.159190 7.361122 5.731709 5.760390 8.650510 11.126741 +B 1.000000 204 62.518082 64.638077 38.465408 76.594894 26.345972 42.123203 91.011017 ++ 1.707239 2.463725 2.176027 4.714409 7.152610 7.032063 15.584165 +B 1.000000 204 1.512276 87.994637 74.551689 106.006203 11.406301 32.183228 44.396530 ++ 1.925664 2.172538 10.050337 5.081652 3.723022 3.102560 8.572049 +L 0.000000 72 59.336029 55.752022 20.743500 72.989319 ++ 4.939338 4.188530 4.539185 6.695642 +L 0.000000 41 0.832856 61.115635 2.500925 78.755638 ++ 3.561104 3.591257 2.196497 7.899208 +L 0.000000 38 6.421627 55.743851 106.514938 71.070595 ++ 2.023701 2.543011 8.010391 4.519025 +L 0.000000 26 117.179970 65.996094 60.491299 68.595459 ++ 1.354007 2.135678 4.162508 3.912361 +L 0.000000 24 76.215294 64.416458 86.315887 69.562035 ++ 1.126295 1.626135 3.851122 4.045342 +L 0.000000 10 62.415859 67.436195 128.951859 72.487610 ++ 4.163362 5.168416 7.087633 5.060684 +S 1.000000 204 65.327133 115.184761 60.098049 63.511250 71.100227 67.999229 ++ 5.470638 11.855494 9.447325 4.109515 5.591525 16.574953 + +27 1 +2 237 +76 207 +16 227 +27 20f +5 21f +5 22f +4 23f +12 217 +5 307 +3 247 +1 327 +2 30f +6 28f +9 287 +2 257 +3 297 +7 24f +4 25f +1 2af +3 27f +3 29f +1 2cf +1 2b7 +1 37f +3 26f +1 277 +1 347 + +41 4 +B 0.000000 199 84.111320 57.078552 25.243114 98.015556 16.771154 42.536533 59.906361 ++ 1.651358 2.993484 2.873858 7.902381 5.749362 19.265596 13.552229 +B 0.000000 197 3.094712 88.090576 76.354568 108.274658 6.791703 28.386999 57.439716 ++ 2.275959 1.776554 6.259202 6.851969 1.906122 2.642387 7.642813 +B 0.000000 118 116.872246 84.238632 23.012966 109.554962 9.635605 35.343304 33.666145 ++ 2.026420 3.135637 2.690923 6.692975 4.417264 5.537215 5.070548 +B 0.000000 39 127.735779 89.317963 22.939070 94.733513 19.172499 35.510296 31.240568 ++ 1.408584 1.858537 1.459496 10.935258 7.637326 3.642623 6.644166 +B 0.000000 8 47.972881 76.241783 121.509666 108.058815 4.109679 33.822392 22.864700 ++ 0.619244 4.870156 7.650138 9.242293 1.620774 1.072993 3.682993 +B 0.000000 5 48.807148 67.095245 97.701988 106.384430 4.470064 34.595173 23.282743 ++ 0.591916 4.524353 4.159610 5.087483 1.173716 1.391314 1.754761 +B 0.000000 1 125.796410 85.927788 24.389816 85.992882 27.902187 58.848324 17.524242 ++ 1.365692 3.109968 4.182205 7.254215 3.094858 3.378358 5.439668 +B 0.000000 1 80.759125 61.313667 25.680670 70.909363 49.696266 115.385849 26.007401 ++ 1.365692 3.109968 4.182205 7.254215 3.094858 3.378358 5.439668 +C 1.000000 200 63.229519 66.230331 30.289942 ++ 1.879259 5.716874 8.847868 +L 0.000000 192 83.914169 55.641682 91.697296 95.060257 ++ 2.977046 10.627006 13.971595 12.314361 +L 0.000000 181 31.634295 85.028618 90.828842 76.687187 ++ 1.991326 2.744702 7.251179 7.867118 +L 0.000000 82 126.348900 53.476219 34.116398 75.339302 ++ 1.120864 2.246949 3.701640 5.412409 +L 0.000000 8 126.378624 74.245155 2.085293 70.134712 ++ 1.825621 1.363639 1.179408 2.505762 +S 0.000000 140 44.998619 117.152397 73.752708 66.399521 68.361237 69.734764 ++ 7.054893 15.448744 7.079320 3.925218 4.657491 9.270427 +S 0.000000 46 44.285259 123.430847 100.144249 67.379700 69.729164 85.208519 ++ 5.548732 6.017478 5.994272 2.113696 4.047546 4.389698 +S 0.000000 13 56.005150 71.783836 18.300507 69.763672 77.570297 18.887737 ++ 3.172995 3.377434 3.871163 1.164404 3.090322 3.999659 +S 0.000000 1 42.545826 115.808685 29.141222 65.778297 88.781525 38.109406 ++ 4.935698 6.707735 5.039891 2.401106 3.931786 4.362525 + +35 1 +17 2f0b +1 2f43 +10 270b +2 330b +4 370b +2 230b +1 3f0b +1 3f09 +1 278a +1 2709 +30 2f07 +25 2707 +2 8707 +11 2f03 +8 8f07 +20 2703 +1 8f03 +2 8703 +3 2307 +28 4707 +2 4527 +2 4727 +5 2b07 +2 2b03 +2 2303 +3 4f07 +1 4f03 +1 4701 +1 2713 +1 10103 +3 4517 +2 4307 +3 4717 +1 4537 +1 2d07 + +51 5 +B 1.000000 200 61.210869 60.414009 44.996590 63.103981 50.278545 35.708595 114.377151 ++ 1.718640 1.943502 2.870689 8.491181 23.464378 7.641634 14.872016 +B 1.000000 200 1.111999 76.345909 96.683189 74.098511 58.369541 75.411606 81.551086 ++ 1.671147 3.384450 7.826042 4.675973 15.033785 26.701540 11.187689 +B 0.000000 2 32.689884 63.660042 124.046150 111.898758 3.834485 31.237671 31.376644 ++ 1.694893 2.663976 5.348366 6.583577 17.293716 17.171587 13.029852 +L 0.000000 187 64.500443 69.497757 130.172852 85.254448 ++ 1.008084 3.570575 8.062130 9.526338 +L 0.000000 155 2.366718 71.960922 108.770233 73.941208 ++ 3.351424 2.532158 8.234741 8.018671 +L 0.000000 126 126.826530 58.092266 67.096779 76.948601 ++ 7.169940 2.348108 8.212796 12.099283 +L 0.000000 74 59.438034 55.733932 26.433929 76.894768 ++ 2.700081 1.955422 4.919174 7.195668 +L 0.000000 54 53.785603 71.931534 92.057236 80.844131 ++ 4.285209 3.254877 7.738161 15.586662 +L 0.000000 48 92.456558 45.467991 93.953995 69.163605 ++ 2.858292 2.365348 5.880444 2.484999 +L 0.000000 45 1.822922 61.097763 3.279848 74.211029 ++ 2.163204 3.922364 2.447758 6.002989 +L 0.000000 1 110.074532 70.077477 70.163643 83.875572 ++ 2.873495 2.700128 6.358140 8.402695 +L 0.000000 1 74.229485 68.471748 25.693626 66.867172 ++ 2.873495 2.700128 6.358140 8.402695 +S 1.000000 200 77.421951 78.633102 43.467968 57.523808 72.026108 60.956718 ++ 10.110652 26.147068 11.925796 3.548957 6.603191 13.015491 + +50 1 +24 103b +13 101b +2 106b +6 105b +3 109b +1 1017 +5 10bb +12 107b +7 100b +7 111b +11 104b +1 10cb +6 102b +20 113b +2 10ab +2 11bb +1 139b +6 117b +4 112b +3 133b +4 10db +2 115b +1 11d7 +3 123b +4 12bb +7 127b +7 10fb +1 12db +2 10eb +1 128b +2 122b +3 129b +2 108b +1 1203 +2 11fb +4 12fb +1 120b +2 121b +2 1293 +1 16db +1 12eb +1 10b3 +2 125b +1 1053 +1 1033 +1 12a3 +2 12b3 +1 12f3 +1 1a3b +1 1223 + +61 6 +B 0.000000 164 1.228640 76.070976 96.442993 77.375511 44.980793 40.129192 88.071663 ++ 1.439309 7.091578 8.922007 8.696263 20.591427 33.041878 17.547029 +B 0.000000 36 0.818107 73.502625 90.553604 61.937122 101.586517 126.713631 84.993439 ++ 1.231028 2.408208 4.538750 1.889076 16.970827 5.256459 11.191558 +C 1.000000 200 65.287361 43.610443 52.020252 ++ 1.337579 3.117970 9.067019 +C 0.000000 1 64.444588 88.991631 27.348124 ++ 1.337579 3.117970 9.067019 +L 0.000000 92 65.420174 68.912102 110.273582 74.447067 ++ 6.267898 4.490405 22.379967 10.074186 +L 0.000000 63 4.156340 73.421585 110.019608 74.176109 ++ 8.965200 2.804563 10.599170 5.975577 +L 0.000000 15 124.245728 65.055214 7.059605 71.547455 ++ 6.679305 6.324702 4.705396 3.312526 +L 0.000000 10 94.413651 41.415028 64.340515 76.055054 ++ 2.812512 2.065167 9.267376 6.594245 +L 0.000000 6 55.411816 75.576576 84.663818 77.445023 ++ 1.483267 1.314856 6.993772 8.104406 +S 1.000000 200 23.120113 61.897499 36.763107 61.464077 66.201889 44.665051 ++ 17.037012 9.489800 12.096966 5.441370 7.996422 11.328998 + +24 1 +43 215 +20 235 +62 205 +5 275 +16 225 +3 315 +1 245 +10 206 +2 256 +3 285 +1 2e6 +10 226 +5 216 +1 20d +2 295 +1 276 +5 236 +1 266 +1 2a6 +3 255 +2 335 +1 395 +1 2c5 +1 2a5 + +71 7 +B 1.000000 201 68.355995 55.908260 79.467102 87.358902 24.954891 6.627673 116.016258 ++ 1.849197 3.602070 8.319350 2.577822 2.966717 5.722599 19.303696 +B 0.000000 38 118.784042 70.835487 58.145630 122.153770 3.535312 32.335533 61.203476 ++ 1.202331 4.189528 2.954317 2.495761 0.646851 0.647756 6.258915 +B 0.000000 1 32.811882 59.523941 137.453323 107.524239 5.731015 29.596058 25.226919 ++ 1.525764 3.895799 5.636834 2.011064 1.806784 3.086214 12.781305 +L 0.000000 200 64.093262 55.779152 130.222183 113.227211 ++ 1.022138 7.458420 8.100091 20.725008 +L 0.000000 198 23.929220 77.183327 67.067146 114.729462 ++ 2.091221 5.014352 13.444718 16.447018 +L 0.000000 174 88.778862 64.165825 59.423752 102.616432 ++ 2.416591 4.827378 11.859543 15.934664 +L 0.000000 64 0.269445 54.615093 114.446663 79.701790 ++ 2.300049 3.037386 5.336898 8.865816 +S 0.000000 150 123.761536 95.884750 5.564672 85.418755 16.332100 1.375934 ++ 9.106649 21.236334 8.814024 3.958646 9.589396 5.078979 +S 0.000000 48 125.625336 50.762218 122.776466 86.522758 60.376965 124.164970 ++ 6.009474 11.926980 8.649642 2.674277 5.214284 8.485798 +S 0.000000 2 120.024185 50.178001 26.961212 82.955193 31.765911 25.502449 ++ 6.298385 16.581657 7.276527 3.316461 7.401840 4.171134 +S 0.000000 1 123.415581 90.519348 73.925003 79.565903 66.523758 71.868607 ++ 6.298385 16.581657 7.276527 3.316461 7.401840 4.171134 + +17 1 +96 b9 +22 f9 +1 439 +3 9b +15 99 +9 bb +5 15b +2 ab +1 8b +15 17b +3 13b +3 159 +2 239 +18 179 +1 b1 +1 fd +4 139 + +81 8 +B 1.000000 200 62.541607 42.315842 70.818733 103.874023 12.669112 33.901688 50.136814 ++ 1.781486 3.445422 6.342821 5.907326 4.298331 4.190469 9.774439 +B 1.000000 200 1.267009 88.763199 71.800964 103.445793 13.556808 33.079441 50.692093 ++ 1.605154 2.214154 6.740967 5.408937 4.584993 4.256919 11.847423 +C 1.000000 200 64.369774 36.792362 45.821983 ++ 1.546361 2.146518 7.453160 +C 0.000000 137 66.036003 92.963844 37.118824 ++ 1.688466 7.387461 6.058814 +C 0.000000 63 68.297966 113.519928 40.549656 ++ 3.809777 2.844204 4.947568 +L 0.000000 25 127.672470 64.071884 3.061007 73.719200 ++ 4.547411 4.258028 3.114196 5.797208 +L 0.000000 13 61.901890 67.774200 132.440231 75.092125 ++ 4.887723 5.422467 5.235678 8.385038 +L 0.000000 1 38.127625 84.724213 51.052650 64.377457 ++ 3.759663 4.189715 3.699157 7.091123 +S 1.000000 200 55.872017 64.630287 65.232956 65.361450 64.659904 64.829765 ++ 5.414144 9.398854 12.204275 2.190376 5.515365 9.260412 + +8 1 +12 12f +117 10f +7 14f +45 117 +12 137 +5 157 +1 18f +1 177 + +91 9g +B 1.000000 200 65.326584 55.053711 37.883667 74.502647 54.793804 59.619804 89.496925 ++ 1.660141 6.226900 2.940191 10.055049 27.200647 40.926956 17.348257 +B 0.000000 1 0.062071 94.565071 70.631088 96.120316 9.289005 32.893616 24.561346 ++ 1.660141 6.226900 2.940191 10.055049 27.200647 40.926956 17.348257 +B 0.000000 1 16.456202 84.337524 113.529846 117.580704 4.274961 32.536903 18.895056 ++ 1.660141 6.226900 2.940191 10.055049 27.200647 40.926956 17.348257 +B 0.000000 1 112.130608 73.977760 33.391029 96.583221 6.494504 32.499195 19.539434 ++ 1.660141 6.226900 2.940191 10.055049 27.200647 40.926956 17.348257 +C 1.000000 200 65.515640 89.014442 51.006935 ++ 1.378231 7.776123 9.423361 +L 0.000000 63 5.095337 63.365803 7.261839 73.245544 ++ 4.876937 6.194809 8.184632 9.139166 +L 0.000000 40 123.376892 59.378281 51.523880 73.725960 ++ 4.049532 4.263789 6.629168 8.294138 +L 0.000000 29 58.801483 55.925789 27.909086 74.757370 ++ 3.957518 1.380517 3.991960 6.198274 +L 0.000000 25 77.692970 58.191204 24.424997 78.756424 ++ 1.069743 2.902693 1.812171 7.762351 +L 0.000000 11 63.445930 63.075001 130.980789 68.891220 ++ 3.000776 3.800809 8.131477 2.170972 +L 0.000000 3 30.365570 89.424232 72.245094 65.754608 ++ 1.733960 1.877425 8.288995 1.817127 +L 0.000000 3 69.578148 57.797642 17.842201 67.975700 ++ 0.516234 3.105954 2.190682 2.720962 +L 0.000000 1 12.768035 61.196270 24.353735 117.434135 ++ 2.334698 3.090448 4.839142 4.927581 +S 1.000000 200 105.804100 68.142082 39.206886 67.228477 63.548634 47.413181 ++ 17.608194 10.286920 18.396107 5.523131 7.423532 14.223130 + +27 1 +40 2031 +17 2051 +2 2811 +71 2011 +2 2231 +3 20b1 +1 2851 +7 2131 +7 20d1 +1 2271 +3 2251 +1 24f1 +1 2211 +16 2091 +1 2035 +5 2071 +1 2411 +1 20d3 +1 22d1 +12 2111 +1 2331 +1 3011 +1 2139 +1 2311 +1 2171 +1 2751 +1 2151 + +:1 : +A 1.000000 200 9.351406 121.505402 83.644173 ++ 33.429165 10.324297 3.772259 +A 0.000000 191 127.377205 121.569832 15.604745 ++ 34.614933 7.088521 3.407418 +L 0.000000 126 63.202023 66.516296 95.239975 91.481430 ++ 6.394967 4.841265 2.696808 16.567959 +L 0.000000 120 95.034576 57.568443 84.843048 87.255226 ++ 7.951179 5.044607 5.087521 14.499930 +L 0.000000 113 30.918570 73.845718 82.741028 86.895210 ++ 4.619862 3.286209 5.777840 15.275126 +L 0.000000 109 95.341385 56.390694 16.031446 87.294518 ++ 7.037479 2.837863 6.162959 14.729472 +L 0.000000 108 30.720234 72.262032 13.801159 87.099648 ++ 7.929480 3.342194 5.463101 15.855967 +L 0.000000 103 126.730278 63.924194 2.637724 89.072708 ++ 4.209045 2.797457 2.312335 15.848318 +L 0.000000 91 63.225788 65.502151 26.932598 87.598160 ++ 4.911147 2.363634 5.471043 14.328959 +L 0.000000 84 127.356644 65.384926 70.921448 88.992706 ++ 4.242314 2.600970 5.179913 15.207298 +L 0.000000 1 118.900238 53.955383 64.350304 128.000000 ++ 5.158767 3.030807 4.300368 15.289128 +S 0.000000 195 62.694435 64.848763 65.810005 63.528122 64.910789 64.146744 ++ 23.962784 14.904617 13.582285 8.243465 8.609654 11.773851 +S 0.000000 3 61.471897 0.249103 44.128433 62.539764 13.962007 39.284927 ++ 5.494121 0.177912 17.937706 2.530202 14.014460 17.968565 +S 0.000000 2 44.882870 123.228699 65.215218 55.440384 100.755173 59.323524 ++ 8.525123 6.299213 13.684923 4.127415 9.996693 13.890054 + +111 1 +1 853 +2 80f +6 87b +3 b5f +2 8d7 +2 b87 +1 a13 +1 9ab +1 a2b +2 9d7 +2 9eb +1 8ab +1 a3b +2 987 +1 9c7 +1 90f +1 a5f +4 883 +4 807 +1 ab3 +1 c9f +2 bdf +2 13ff +2 827 +1 887 +3 863 +2 a7b +2 8ef +3 81f +2 83f +4 9f7 +1 8bf +1 847 +1 9d3 +3 9ef +3 aab +2 bbf +22 bff +4 aff +1 8c7 +2 b9f +3 bf7 +3 957 +2 9fb +2 bab +1 bdb +1 b6f +1 8ff +1 87f +1 93f +4 8df +3 a1d +1 a1f +1 af7 +1 aaf +1 8e7 +1 b67 +1 89f +1 8f7 +1 80d +1 b0b +1 abf +1 b2b +2 a9f +1 873 +1 bf3 +1 93b +1 85f +1 96f +1 8af +1 b73 +1 b97 +2 84b +1 9e3 +1 943 +3 86f +5 803 +1 b57 +1 a1b +1 be7 +1 b8f +1 a77 +1 9c3 +1 9cf +1 98b +1 843 +1 907 +1 a0b +1 893 +4 813 +1 ae3 +1 101f +1 a6b +1 9a3 +1 a03 +1 b23 +1 83b +1 a27 +1 805 +1 90b +2 801 +1 23ff +2 903 +1 a09 +1 809 +1 833 +2 823 +1 91f +1 200f +2 877 +2 92f + +;1 ; +B 0.000000 142 64.739197 57.565929 -5.554220 105.045052 13.118443 33.954735 36.816658 ++ 2.505254 2.780560 5.817530 9.659440 6.503871 3.833364 7.812295 +A 0.000000 147 126.654564 122.307579 82.975883 ++ 20.255537 5.733072 4.815958 +A 0.000000 62 51.785740 83.941971 10.726496 ++ 6.798558 5.986316 6.419576 +A 0.000000 49 62.906799 118.134598 84.831497 ++ 6.548546 6.460461 3.888792 +L 0.000000 163 26.308163 71.519318 1.138934 96.253471 ++ 5.384140 2.911125 9.824449 20.070658 +L 0.000000 146 63.112961 65.526360 28.949686 96.336632 ++ 5.202074 2.154694 8.900419 17.244740 +L 0.000000 140 96.852356 59.360958 7.198184 93.290138 ++ 8.658330 2.277796 14.763997 19.214609 +L 0.000000 136 127.338409 66.515953 72.334984 92.340752 ++ 11.069538 5.717049 6.463275 12.950511 +L 0.000000 134 93.218620 59.044460 85.798393 93.125061 ++ 4.736876 3.998597 4.838893 15.721684 +L 0.000000 126 62.116734 67.963257 95.886040 93.639557 ++ 5.625843 3.772661 3.984136 15.105917 +L 0.000000 123 30.627474 74.160286 83.659103 90.501297 ++ 7.089418 3.878398 5.520018 14.554517 +L 0.000000 60 119.125954 61.995201 -9.816473 82.004311 ++ 5.261906 3.109286 7.556039 11.779619 +L 0.000000 24 13.650417 66.361458 -21.531523 95.692970 ++ 2.717817 2.214771 5.138346 21.956083 +S 0.000000 113 100.653465 65.252655 73.931931 68.089951 69.558296 81.747017 ++ 17.938562 30.877563 14.673541 5.594598 11.093388 20.003712 +S 0.000000 66 84.196686 127.827553 81.791016 68.843361 120.802429 71.881729 ++ 12.351664 4.981687 13.649169 4.980305 9.491731 12.720121 +S 0.000000 15 102.149506 83.006790 28.266644 63.619274 66.994774 38.599838 ++ 12.753816 18.489523 4.569617 4.533717 7.299246 6.133689 +S 0.000000 1 71.762802 0.199345 80.246941 57.044308 40.904453 86.050446 ++ 14.348014 13.774190 10.205822 4.759523 8.678489 10.915092 +S 0.000000 1 70.452545 128.000000 52.192902 66.866669 128.000000 2.921373 ++ 14.348014 13.774190 10.205822 4.759523 8.678489 10.915092 + +130 1 +1 4276 +2 43f6 +2 4076 +2 4af6 +3 48f6 +5 4876 +3 4c76 +3 44f6 +1 49f6 +1 204f6 +2 4b76 +1 4a76 +1 41f6 +3 4ff6 +3 4376 +1 4ef6 +1 45f6 +1 2776 +2 4bf6 +1 40f6 +1 4e76 +1 21f6 +1 2ff3 +10 27f9 +2 26f3 +1 2ff7 +11 27f3 +3 2773 +1 37f7 +3 27b9 +1 2033 +3 37f3 +2 25f3 +1 25e7 +1 2673 +1 2ef9 +1 27f7 +1 2fb3 +1 27bd +1 2fb9 +3 37f9 +2 27b7 +3 27b3 +3 27e3 +1 2433 +4 47f6 +1 2576 +1 4f83 +1 4db3 +1 4573 +1 4723 +1 53b3 +1 57b3 +1 4576 +3 47f3 +1 4ff3 +2 57f3 +1 24f3 +1 2733 +1 2ef6 +1 37fd +2 27fc +2 23f9 +1 27ec +1 37fc +1 2d59 +1 103fc +1 2073 +1 25dc +1 8913 +1 3b89 +1 8803 +1 9b13 +1 8853 +1 27d9 +1 8f99 +1 8ab3 +1 8fb3 +1 8013 +1 3903 +1 8fe9 +1 95a3 +1 8da3 +1 9ec9 +1 2fa9 +1 8f83 +1 8fa9 +1 9f89 +1 3fe9 +1 2213 +1 2ab3 +2 2913 +1 2c93 +1 2413 +1 2109 +1 2633 +1 2113 +1 2483 +1 2513 +1 20b9 +1 2b13 +1 3883 +2 2593 +2 22d3 +1 2919 +1 20c3 +1 2103 +1 5159 +1 47f9 +1 3149 +1 40d3 +1 40f3 +1 40c9 +1 4243 +2 2753 +1 4053 +1 42d9 +1 4193 +1 26f9 +1 47c3 +1 4063 +2 2053 +1 22c3 +1 35f3 +1 21e3 +1 2813 +1 2f99 +1 23e3 +1 23d9 +1 2223 + +<1 < +B 1.000000 200 0.515020 81.154800 56.245872 85.263153 36.580261 33.577835 116.231071 ++ 0.517597 2.216462 5.247228 1.470966 3.556452 4.747992 12.955986 +L 1.000000 200 119.252251 64.063217 30.243101 127.747124 ++ 0.771740 2.570670 3.900261 8.738476 +L 1.000000 200 73.162231 65.622459 85.632011 123.798882 ++ 1.039728 3.013141 8.098700 7.573296 +L 0.000000 190 10.597973 75.076691 74.531303 100.677414 ++ 1.051623 2.796646 6.745660 14.507954 +L 0.000000 180 54.011543 75.054573 38.491024 104.884285 ++ 0.730749 2.182925 4.545578 11.876400 +S 1.000000 200 72.158836 70.744225 62.255936 65.129829 70.437714 65.079529 ++ 10.171926 20.340382 9.018165 2.878774 5.145015 6.819506 + +3 1 +180 3f +10 2f +10 27 + +=1 = +B 0.000000 11 96.593887 71.694122 53.626980 113.375328 3.997582 32.348595 31.840921 ++ 0.215693 7.709425 17.512051 5.635925 1.908081 2.156935 7.756507 +B 0.000000 8 32.520779 57.988121 52.321384 110.432083 4.847179 32.926266 33.066891 ++ 0.201853 7.893218 3.943527 2.651334 1.722338 2.521195 9.763376 +B 0.000000 6 32.456017 67.199745 86.149101 121.031097 2.761187 32.068996 38.516460 ++ 0.175710 2.476570 9.375994 2.070801 0.224740 0.438292 2.707765 +A 0.000000 196 127.998848 57.278496 41.847874 ++ 1.995317 6.694685 4.514893 +A 0.000000 190 0.156678 57.185215 77.509125 ++ 1.563682 7.201234 8.191625 +L 0.000000 197 127.981544 64.979088 33.721497 116.977310 ++ 1.177131 7.517466 5.387760 23.330963 +L 0.000000 196 0.113448 65.054764 69.162949 116.103287 ++ 1.483968 6.024610 5.212936 22.137287 +L 0.000000 196 64.041054 65.541115 85.505127 118.167755 ++ 1.378772 4.086012 8.063151 15.505498 +L 0.000000 195 64.005341 66.027939 49.950108 116.892609 ++ 1.744413 7.278917 4.869066 24.232700 +L 0.000000 139 94.942696 35.863277 78.586655 95.696327 ++ 8.097963 3.190517 6.767209 14.791348 +L 0.000000 137 29.068762 95.045486 76.148529 97.949059 ++ 6.592730 3.299838 5.910019 15.368039 +L 0.000000 129 29.762098 95.209938 41.299671 96.298958 ++ 7.007225 3.306789 7.261426 15.352708 +L 0.000000 127 94.149643 35.717056 43.016411 95.969643 ++ 7.366994 3.089817 5.313863 16.523289 +L 0.000000 1 23.151447 93.123322 36.034042 96.180031 ++ 4.108951 2.779236 5.820833 12.078882 +S 0.000000 194 60.483040 64.107269 50.668686 62.325958 64.417366 50.195057 ++ 30.942358 15.572992 15.739038 13.506615 5.496104 15.793804 +S 0.000000 4 1.176189 64.355164 64.464691 5.766901 64.316864 64.817047 ++ 1.253236 0.062887 0.352695 9.078446 0.193912 0.071625 +S 0.000000 1 104.172157 128.000000 30.809809 75.046425 90.235329 43.069099 ++ 13.519267 6.520190 8.045867 11.292530 2.005326 7.932714 +S 0.000000 1 62.268608 0.493789 72.524414 64.366821 41.857754 64.958031 ++ 13.519267 6.520190 8.045867 11.292530 2.005326 7.932714 + +43 1 +68 5ff8 +1 4db9 +6 57f8 +1 5fd1 +7 53f8 +16 41f8 +5 51f8 +10 5df8 +6 4df8 +7 43f8 +4 59f8 +1 5ff9 +8 45f8 +3 9ff8 +11 5bf8 +10 4ff8 +1 12bf8 +1 52eb +1 49e9 +1 4466 +1 5fb9 +9 47f8 +1 43a9 +1 87f8 +1 4fec +1 49d9 +1 53e9 +1 4772 +1 53d8 +1 5d6c +1 59ec +1 44fa +1 49f8 +1 49fc +1 4f6c +1 53fa +1 4bf8 +1 4ff9 +1 46fa +2 55f8 +1 4ff2 +1 217a9 +1 58fa + +>1 > +B 1.000000 200 64.542274 49.426830 56.741634 85.103127 36.837086 34.120186 116.101791 ++ 0.870261 2.708642 5.425422 1.643863 2.937977 8.251997 12.424066 +L 1.000000 200 54.749832 68.091736 82.503441 125.215866 ++ 0.782269 2.732099 7.753098 11.699929 +L 1.000000 200 9.496292 65.777473 29.245029 126.544853 ++ 1.275450 5.115372 4.129728 16.666008 +L 0.000000 188 74.421829 55.651421 38.728149 100.658600 ++ 0.993552 2.751440 6.003137 13.708177 +L 0.000000 183 117.943169 55.421238 75.343582 104.469009 ++ 0.741301 2.956351 6.133693 13.663843 +S 0.000000 185 66.319664 57.434643 63.802059 65.001289 58.107983 63.006638 ++ 12.896584 10.110854 14.366110 3.065215 3.837105 13.877687 +S 0.000000 15 66.811920 13.882116 64.998199 66.641930 47.933926 62.874432 ++ 4.784781 7.626664 6.142734 2.018421 1.511564 3.547790 + +7 1 +180 3f +3 27 +7 47 +2 57 +5 4f +2 2f +1 5f + +?1 ? +B 0.000000 195 73.728569 63.054024 95.134529 68.952759 36.648262 10.314394 95.258942 ++ 4.716786 4.323405 5.950318 4.877034 7.144903 13.716969 14.850121 +B 0.000000 85 113.407959 73.355118 57.954952 111.637146 6.427322 32.278397 27.956448 ++ 2.976469 3.788556 6.204677 4.464639 2.369969 1.810617 7.359671 +B 0.000000 13 121.244843 74.833961 43.856529 105.004211 9.448465 30.880554 70.797920 ++ 1.057935 3.934217 3.946387 5.423072 1.955652 2.796867 7.341393 +B 0.000000 5 65.800407 64.730476 111.724571 87.482376 18.456684 0.638916 71.389320 ++ 0.212235 0.569809 2.111853 2.774896 1.904685 0.366814 6.560619 +B 0.000000 1 72.458397 61.321789 34.247322 96.648170 17.727625 45.500492 18.650026 ++ 1.756526 2.594389 4.553309 4.181701 3.343802 4.672817 9.027951 +B 0.000000 1 112.192162 66.264610 43.959221 107.964935 8.749546 37.486534 24.654079 ++ 1.756526 2.594389 4.553309 4.181701 3.343802 4.672817 9.027951 +A 0.000000 184 111.942871 121.519150 15.267513 ++ 37.399963 8.807236 3.116121 +L 0.000000 135 31.018539 69.900223 14.244343 96.428566 ++ 6.493922 6.823693 5.656098 15.262012 +L 0.000000 132 94.893921 55.695126 15.538651 94.385551 ++ 6.135560 5.772672 4.756172 14.257762 +L 0.000000 129 125.755272 61.592548 3.744099 97.345497 ++ 7.032510 5.818135 3.400379 14.085710 +L 0.000000 123 61.841667 63.552971 26.573492 95.160408 ++ 7.615647 6.186320 6.226742 15.820618 +L 0.000000 114 87.122429 66.388809 77.219803 87.278496 ++ 4.870653 2.895450 7.508984 16.060585 +L 0.000000 76 19.099115 75.834160 69.233208 86.876289 ++ 5.122041 3.705558 6.870023 12.299377 +L 0.000000 50 64.177582 64.975151 134.248611 71.529541 ++ 4.175207 3.502509 7.526443 5.163004 +L 0.000000 7 5.971188 56.225727 110.557518 81.361359 ++ 1.215958 0.614899 3.234390 2.183530 +L 0.000000 3 57.196556 67.718353 137.684052 99.572906 ++ 0.903677 8.496975 1.111254 5.008586 +L 0.000000 1 28.715969 90.017113 82.217346 65.269569 ++ 4.253665 4.868468 4.923335 11.126799 +S 0.000000 141 124.573112 110.553024 30.914684 77.193893 63.488846 30.006723 ++ 7.770325 11.997872 9.679010 5.345648 9.667531 11.126576 +S 0.000000 35 122.802605 88.917732 67.996193 78.635361 72.543388 69.707062 ++ 5.974566 14.062636 8.585676 3.879162 4.331668 10.485872 +S 0.000000 15 126.372101 73.530556 98.927483 81.017212 60.995899 94.128006 ++ 3.162760 9.057278 5.215297 2.780039 3.970400 4.846237 +S 0.000000 6 123.980637 95.270805 0.491001 74.875404 29.064159 0.459787 ++ 4.847346 6.710359 0.260926 1.954634 3.824126 0.294700 +S 0.000000 1 128.000000 34.633369 30.883720 61.641342 33.516800 38.282520 ++ 5.114985 10.457036 5.935227 3.489871 5.045617 6.688346 +S 0.000000 1 128.000000 0.585118 22.354849 84.796814 13.359494 55.354103 ++ 5.114985 10.457036 5.935227 3.489871 5.045617 6.688346 +S 0.000000 1 115.675713 54.642624 128.000000 75.708664 62.120575 128.000000 ++ 5.114985 10.457036 5.935227 3.489871 5.045617 6.688346 + +119 1 +1 20dc3 +1 200c3 +1 24843 +1 20243 +2 20c43 +1 24343 +1 204c3 +2 209c3 +1 24443 +1 20543 +1 22043 +1 20143 +1 24ac3 +2 20cc3 +11 207c3 +1 249c3 +1 247c3 +1 205c3 +1 20a43 +1 22741 +2 202c1 +1 206c1 +1 233c1 +1 330c1 +2 223c1 +1 20541 +1 22041 +1 226c1 +5 217c1 +2 230c1 +1 220c3 +1 23741 +4 207c1 +2 21441 +1 22341 +1 20241 +9 20fc3 +1 203c3 +3 1007c1 +2 21fc3 +1 225c3 +1 213c1 +1 1003c1 +1 20f43 +1 20ec3 +1 22fc3 +10 21fc1 +1 20b43 +1 206c3 +2 227c1 +9 20fc1 +2 40ec3 +1 41fc3 +2 417c3 +6 40fc3 +3 407c3 +1 42bc3 +1 40bc3 +2 403c3 +1 427c3 +1 40fe3 +2 42005 +2 43005 +5 40005 +1 43015 +1 100fc1 +1 22803 +1 40003 +1 247c1 +1 41005 +1 219c1 +1 21941 +1 23dc1 +1 21b41 +1 22b41 +3 21dc1 +1 23d41 +3 21bc1 +1 21ec1 +4 21841 +1 239c1 +2 22fc1 +1 22e43 +1 20041 +1 20043 +2 20d43 +1 21cc1 +1 20843 +1 23b41 +1 22843 +1 23ec1 +1 218c1 +1 23a43 +1 23043 +1 229c3 +1 22a43 +1 81a48 +1 208805 +1 83141 +1 83f41 +1 801005 +1 41801 +1 81c48 +1 81741 +1 49848 +1 82bc3 +1 838c8 +3 81fc1 +1 81941 +2 83fc1 +1 401cc8 +1 83a41 +1 838c1 +1 22dc1 +1 23fc1 +1 28fc1 +1 21f41 +1 102fc1 +1 41e43 + +@1 @ +B 0.000000 171 122.506691 64.055138 60.663807 55.827244 105.254448 122.132660 126.694412 ++ 1.624622 1.584089 4.876163 21.286682 46.094810 18.578215 7.891772 +B 0.000000 16 122.480240 69.397827 24.127602 70.406723 73.130440 41.424675 95.086739 ++ 0.949343 2.183903 3.548000 1.851177 6.292750 4.979185 9.755862 +B 0.000000 11 121.333351 104.125870 13.559196 101.518875 12.935726 25.002901 45.148594 ++ 0.465650 2.637249 4.857139 11.779023 7.348403 12.020745 7.948229 +B 0.000000 4 48.151863 33.292000 112.585777 96.532455 9.702400 32.355816 14.495278 ++ 0.090145 2.633908 4.908624 0.077453 0.308371 0.358891 0.908822 +B 0.000000 1 80.490517 43.253983 8.752691 118.339890 4.960888 32.568470 17.425446 ++ 0.782440 2.168791 4.547482 5.502512 11.183768 7.291915 5.794978 +B 0.000000 1 105.736565 65.778702 86.906479 112.987587 9.139418 30.365551 39.428696 ++ 0.782440 2.168791 4.547482 5.502512 11.183768 7.291915 5.794978 +B 0.000000 1 119.209946 66.488579 43.384975 86.682953 15.717191 128.000000 96.980911 ++ 0.782440 2.168791 4.547482 5.502512 11.183768 7.291915 5.794978 +B 0.000000 1 0.950925 93.768433 74.397057 110.827095 3.493780 34.179661 28.337599 ++ 0.782440 2.168791 4.547482 5.502512 11.183768 7.291915 5.794978 +C 0.000000 196 65.853279 53.833649 46.134869 ++ 4.824965 5.993156 9.719930 +C 0.000000 14 64.456467 99.374176 41.704739 ++ 1.170322 3.220735 2.073760 +C 0.000000 8 64.299622 52.621799 107.917786 ++ 1.489631 2.336215 4.889957 +C 0.000000 4 50.645920 63.286179 7.432443 ++ 1.360467 3.812028 0.401186 +A 0.000000 1 72.542274 116.319534 63.417599 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 49.264912 81.658699 39.488049 ++ 8.000000 8.000000 4.000000 +L 0.000000 32 34.126404 92.689133 74.569023 71.601562 ++ 0.880360 6.016873 6.023826 21.252184 +L 0.000000 15 127.268135 74.911781 34.010036 72.230125 ++ 0.930544 1.924152 6.536073 5.078243 +L 0.000000 6 124.972694 66.222282 0.732794 74.212646 ++ 1.474759 1.029746 7.621875 5.017943 +L 0.000000 3 76.511513 68.095268 81.393524 69.002663 ++ 3.255760 0.442325 4.706336 3.610877 +L 0.000000 3 66.370277 65.705086 128.629745 70.307289 ++ 0.665643 2.038288 0.528231 4.008475 +L 0.000000 1 89.712730 54.940895 97.313507 102.082672 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 40.971252 76.646286 110.594902 81.649734 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 60.053192 74.939583 12.366617 67.240082 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 24.557510 71.054459 37.371780 119.238365 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 90.820862 57.246223 35.153969 69.295013 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 118.025490 53.491390 0.822920 68.497246 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 3.017707 71.181992 92.476112 87.990242 ++ 1.409779 1.856326 4.745972 5.203329 +S 0.000000 198 65.967476 64.096825 69.385712 66.140793 62.130402 65.744484 ++ 12.071730 7.270923 11.187209 2.517528 2.388455 9.255417 +S 0.000000 1 97.248604 62.023312 108.433823 78.314735 61.572781 104.636093 ++ 8.383145 7.270923 6.474079 2.097940 1.990379 6.427372 +S 0.000000 1 67.545525 40.209602 64.830978 67.340813 42.253262 69.776711 ++ 8.383145 7.270923 6.474079 2.097940 1.990379 6.427372 + +27 1 +1 4000b02 +1 400cb02 +1 401c302 +2 4004302 +1 a1ad020 +6 400c302 +1 4004b02 +1 4008b02 +2 400c102 +1 402c101 +1 4204302 +2 4008101 +1 4024101 +3 4044101 +1 4004111 +144 4000101 +1 5004101 +1 4010101 +7 4004101 +3 4014101 +1 4000181 +8 4000504 +3 4000104 +4 4000109 +1 4000001 +1 10c12000 +1 4000041 + +A1 A +B 0.000000 208 96.316063 63.893112 22.618700 72.181854 20.233335 34.691311 105.981575 ++ 0.744736 3.175496 3.910997 15.645009 8.980779 8.039799 18.373259 +B 0.000000 71 56.802372 40.335400 61.244160 103.850266 7.073497 27.483257 84.876694 ++ 5.652114 5.189891 11.149862 12.380412 3.843987 5.320408 20.327572 +B 0.000000 40 6.291943 99.778046 54.910435 112.685272 4.249992 30.274515 72.276741 ++ 3.706388 7.145343 2.520241 6.370249 1.412957 1.557335 12.773904 +B 0.000000 6 48.959095 76.910027 101.103752 112.086761 3.134143 33.854645 32.140625 ++ 0.580933 2.516895 3.924657 5.209663 1.266183 0.988570 5.540562 +B 0.000000 1 48.427467 96.406242 136.526047 105.553131 11.237039 26.920885 12.554969 ++ 2.614153 4.506907 5.376440 9.249458 3.189945 3.641536 14.253824 +B 0.000000 1 96.237152 66.021057 21.709019 32.371456 48.286743 29.907265 66.514366 ++ 2.614153 4.506907 5.376440 9.249458 3.189945 3.641536 14.253824 +B 0.000000 1 96.990273 30.171822 1.255657 107.760979 8.572666 37.738800 24.824615 ++ 2.614153 4.506907 5.376440 9.249458 3.189945 3.641536 14.253824 +B 0.000000 1 96.283730 93.430687 1.400948 114.151123 4.491150 32.072880 28.574284 ++ 2.614153 4.506907 5.376440 9.249458 3.189945 3.641536 14.253824 +C 1.000000 209 65.803818 71.588715 30.791933 ++ 6.044244 5.630033 6.313878 +L 0.000000 207 39.100563 89.963997 69.613792 125.055702 ++ 3.622388 7.188418 6.259457 12.391061 +L 0.000000 189 88.361511 43.502689 69.148071 121.676567 ++ 1.548054 3.327184 8.932471 19.591127 +L 0.000000 14 81.713173 62.583176 79.459099 127.439568 ++ 0.433772 1.304176 2.679372 1.404095 +L 0.000000 2 0.713078 85.202316 4.013545 66.084656 ++ 1.666828 3.207772 5.460852 6.662910 +L 0.000000 2 1.729427 64.016129 38.568779 66.686203 ++ 1.666828 3.207772 5.460852 6.662910 +L 0.000000 1 42.249836 77.937729 100.905426 74.333069 ++ 1.666828 3.207772 5.460852 6.662910 +L 0.000000 1 62.139999 57.449760 114.645081 68.369476 ++ 1.666828 3.207772 5.460852 6.662910 +L 0.000000 1 81.833656 80.961197 109.546906 89.935555 ++ 1.666828 3.207772 5.460852 6.662910 +S 0.000000 169 31.484734 68.363113 59.055912 56.873096 65.160614 55.953476 ++ 15.284576 11.667940 4.103404 6.413703 2.396883 7.847456 +S 0.000000 20 42.939911 123.505997 64.190010 63.112179 86.586922 93.747276 ++ 3.974175 7.064520 2.108468 1.697289 1.748275 3.657987 +S 0.000000 15 16.852478 128.000000 61.540859 47.690670 102.619225 126.168594 ++ 7.218469 0.001000 3.064914 2.137982 3.109987 2.511612 +S 0.000000 5 16.187675 128.000000 106.043633 49.611691 77.396278 123.067024 ++ 8.416251 0.001000 1.167688 2.669141 1.850451 2.712623 + +18 1 +37 20703 +12 20707 +1 20722 +1 28703 +104 20701 +2 22701 +1 24501 +20 40701 +1 20781 +7 20705 +2 21701 +3 10030f +1 11030f +13 80b07 +1 10031f +1 80907 +1 8030f +1 20745 + +B1 B +B 1.000000 211 1.403663 95.805351 74.702324 103.592613 12.892900 32.987144 47.689575 ++ 1.724000 3.032807 6.674688 7.819651 6.209797 3.075579 9.238050 +B 0.000000 133 63.816994 34.570019 70.221817 95.479027 4.859993 32.318901 98.103905 ++ 2.209327 3.288670 7.734440 11.101601 1.245648 1.608899 24.678705 +B 0.000000 1 96.318504 54.125053 3.144890 107.893852 2.757953 34.022343 38.005268 ++ 1.782552 3.160738 7.204564 7.340823 2.675199 2.342239 11.759852 +C 1.000000 211 67.532166 99.406837 47.308079 ++ 4.460047 7.936177 6.472407 +C 1.000000 211 66.959282 40.832428 54.326817 ++ 2.462936 3.572337 6.918605 +L 0.000000 209 95.546753 37.334927 69.710060 107.853210 ++ 2.499515 3.912666 11.519524 17.524904 +L 0.000000 208 63.777424 58.485146 133.775208 90.154671 ++ 1.266997 4.298501 7.443225 12.377235 +L 0.000000 196 1.080878 60.779232 3.553115 94.493317 ++ 2.092072 5.769166 2.188927 12.480210 +S 1.000000 211 59.831955 65.262459 57.823692 64.395554 66.252701 60.732136 ++ 3.591205 14.267024 3.625319 2.389421 9.819136 4.517374 + +8 1 +118 1fb +2 1db +10 17b +73 1f9 +1 1b9 +4 179 +2 1bb +1 17f + +C1 Cc +B 1.000000 213 127.727112 69.809525 68.698418 63.042671 32.208012 35.019615 128.000000 ++ 1.552980 2.960287 4.843616 7.655548 12.732700 6.556470 0.001000 +B 0.000000 23 30.500452 89.960777 136.118744 107.540283 8.394455 32.690823 27.638937 ++ 1.354841 4.670982 6.466877 4.406224 2.100674 1.472641 4.939666 +B 0.000000 2 48.764900 43.202774 124.850357 101.327370 8.356269 33.985245 15.041504 ++ 1.453910 3.426386 5.655247 5.663701 6.180573 4.014556 2.058694 +B 0.000000 1 0.178500 104.142128 118.061165 115.504555 6.460431 32.209927 25.397881 ++ 1.453910 3.426386 5.655247 5.663701 6.180573 4.014556 2.058694 +B 0.000000 1 112.472908 91.096222 13.652380 120.540131 3.539090 32.864220 20.666195 ++ 1.453910 3.426386 5.655247 5.663701 6.180573 4.014556 2.058694 +L 0.000000 18 65.267624 72.636337 137.602310 80.095535 ++ 4.622468 8.278273 6.940357 10.919986 +L 0.000000 13 68.903297 79.644485 25.555418 73.936935 ++ 1.233095 3.566484 5.492428 2.710044 +L 0.000000 9 126.090141 66.875763 5.349281 74.491577 ++ 1.071400 4.242560 2.351363 8.658561 +L 0.000000 6 33.210884 48.228733 70.126770 68.072556 ++ 1.589808 1.547420 6.603779 3.152846 +L 0.000000 1 2.313071 74.094574 130.549194 66.159554 ++ 1.776088 4.063756 5.057800 6.360359 +S 0.000000 122 67.629616 48.271019 69.292404 64.870308 60.108387 65.948067 ++ 7.181920 9.192354 12.528640 3.096859 4.118094 9.535118 +S 0.000000 87 68.867981 5.089407 81.157494 66.542175 58.748302 79.174759 ++ 5.953879 6.108608 9.715478 1.992645 6.203818 8.636871 +S 0.000000 4 64.040199 1.982716 101.431412 65.534790 51.948372 104.522255 ++ 2.998433 2.308934 2.766057 1.434392 0.717879 3.628434 + +25 1 +4 403 +105 401 +5 441 +2 501 +1 461 +1 481 +1 411 +1 405 +1 421 +1 883 +17 803 +40 801 +1 807 +12 821 +1 809 +1 921 +2 901 +4 841 +3 1001 +1 1041 +2 8a1 +4 881 +1 4c1 +1 961 +1 a01 + +D1 D +B 0.000000 124 63.725456 29.240408 69.735573 98.078758 4.309765 32.417637 96.065536 ++ 2.001985 4.466132 7.859338 7.375380 0.882824 1.494936 16.426075 +B 0.000000 3 96.668015 39.816582 2.207380 105.110886 5.344478 34.506676 33.582340 ++ 0.328255 3.006864 0.953788 1.783364 1.780691 0.277335 8.266916 +B 0.000000 3 32.283714 43.094425 139.509384 107.630524 5.166416 30.382307 33.205372 ++ 0.059810 5.272151 0.693828 1.387663 1.089351 0.442519 5.958887 +B 0.000000 2 64.594559 33.641876 65.235405 114.589676 3.314239 33.518677 34.798187 ++ 0.796683 4.248383 3.278826 3.515469 1.250955 0.738263 10.217293 +B 0.000000 1 32.214050 58.512325 138.457855 96.106598 9.891491 32.743149 20.467108 ++ 0.796683 4.248383 2.732355 3.515469 1.250955 0.738263 10.217293 +B 0.000000 1 64.123116 36.915024 24.327473 102.932098 11.464586 39.894230 14.508495 ++ 0.796683 4.248383 2.732355 3.515469 1.250955 0.738263 10.217293 +C 1.000000 205 66.983566 68.272964 91.106674 ++ 3.014141 4.729416 11.437462 +L 0.000000 202 95.496803 32.928974 69.801003 109.442352 ++ 2.371451 3.649918 6.655528 15.853985 +L 0.000000 186 1.175881 56.426819 3.655718 84.606247 ++ 1.912279 7.293790 2.222452 10.197557 +L 0.000000 182 63.407139 55.454166 132.576294 84.412376 ++ 1.127162 4.529580 7.787099 11.291574 +L 0.000000 1 30.632139 99.745369 67.413292 72.802345 ++ 1.403338 4.133480 5.185275 11.566928 +S 1.000000 205 62.853710 61.980221 63.940147 63.384159 65.720207 63.476105 ++ 3.970921 13.480095 4.679900 2.572918 9.905648 5.085258 + +14 1 +86 bc1 +11 ac1 +1 b68 +2 b41 +1 8c1 +73 bc0 +4 ac0 +1 bc8 +1 9c0 +1 fc0 +1 9d5 +18 9c1 +3 ac3 +2 9c5 + +E1 E +B 1.000000 208 0.621546 68.466873 39.886723 61.427719 39.579971 41.489647 106.648239 ++ 1.863834 2.795044 2.569830 7.257325 9.399488 12.137126 20.963348 +B 0.000000 206 0.321815 70.675499 99.803192 61.443638 47.657448 22.407839 98.232826 ++ 1.797525 3.836799 7.652798 6.337375 14.034439 12.042994 22.186701 +B 0.000000 130 63.727360 34.363403 70.129875 92.562408 5.205521 32.117378 101.668121 ++ 1.929142 4.331925 7.182187 9.865731 1.258508 1.754761 21.067539 +B 0.000000 5 32.502869 59.963596 134.832245 117.822739 4.510786 33.437969 33.285454 ++ 0.207256 6.635684 5.674180 7.668483 3.288104 2.272352 9.625632 +B 0.000000 2 96.239334 55.961525 2.035296 122.069427 2.170134 32.041275 43.396461 ++ 1.449439 4.399863 5.769749 7.479840 6.995135 7.051808 16.851479 +C 0.000000 2 70.535126 107.419189 59.296257 ++ 2.000000 2.000000 4.000000 +L 0.000000 206 0.010874 64.496658 2.013095 113.887611 ++ 2.209294 7.131270 1.521673 23.133926 +L 0.000000 204 95.485390 38.135506 69.416565 110.297737 ++ 2.582448 4.258591 7.954324 15.714702 +L 0.000000 201 64.076279 63.641823 133.732773 112.112831 ++ 1.335239 7.723197 7.156462 16.732857 +L 0.000000 93 63.654114 74.653572 21.340683 90.343544 ++ 1.790622 4.153300 2.668988 11.107650 +L 0.000000 82 1.756448 70.962715 111.522789 90.723465 ++ 1.059597 1.560074 5.032974 9.865894 +L 0.000000 78 63.173588 71.947174 80.947220 82.914848 ++ 2.123588 2.401040 6.291822 8.052228 +L 0.000000 78 1.726019 69.248764 58.578114 84.758827 ++ 1.220919 1.852264 3.289860 8.044182 +L 0.000000 3 30.193922 90.419579 87.184395 73.237129 ++ 3.059808 8.108371 8.601010 4.974157 +L 0.000000 1 87.962929 38.516815 43.179867 67.309097 ++ 1.364254 3.430180 4.675755 9.010175 +S 0.000000 206 68.495033 25.492044 63.611816 60.290329 62.454517 61.155697 ++ 9.316971 17.635366 7.842354 4.290982 11.328997 8.062267 +S 0.000000 2 114.252594 53.237236 74.921906 56.353523 62.303787 71.441406 ++ 7.764142 17.635366 6.535295 4.290982 11.328997 6.718555 + +22 1 +93 81c7 +14 83c7 +3 85c7 +1 8187 +1 85cf +2 9fcb +68 9fc3 +2 9fc7 +2 9dc3 +1 8fc3 +2 9bc3 +2 97c3 +1 87c3 +1 81d7 +2 80cf +1 a1c7 +4 80c7 +1 8397 +1 c147 +2 121e5 +3 8147 +1 88c7 + +F1 F +B 1.000000 206 115.301155 64.790070 40.196339 86.612831 25.795191 44.101723 94.240715 ++ 3.066037 3.573278 4.252824 13.558330 17.920603 13.213746 20.968071 +B 0.000000 202 121.727669 73.625572 99.819145 63.419048 56.594772 18.551991 105.468399 ++ 2.978142 3.424048 7.121233 5.845968 28.597666 28.384043 20.761259 +B 0.000000 129 63.758106 35.553059 69.545395 92.898392 5.430004 32.409382 103.048729 ++ 1.849876 2.877703 6.548615 12.393125 1.596354 1.331320 21.808432 +B 0.000000 4 118.529373 84.708405 59.072334 90.904106 17.673323 51.688282 40.018673 ++ 0.337701 1.058946 1.037020 4.753842 4.257622 6.630455 0.955953 +B 0.000000 2 96.683136 53.956264 2.130140 115.241287 4.253969 34.068897 28.603588 ++ 2.057939 2.733494 4.562722 8.621436 9.021046 8.715953 12.087753 +B 0.000000 2 32.179672 55.954041 136.227737 115.950554 4.339798 30.906315 29.935238 ++ 2.057939 2.733494 4.562722 8.621436 9.021046 8.715953 12.087753 +B 0.000000 1 64.425217 38.457798 49.498699 110.232040 2.918109 31.152859 34.521049 ++ 2.057939 2.733494 4.562722 8.621436 9.021046 8.715953 12.087753 +C 0.000000 4 71.548744 84.474884 28.010422 ++ 0.968694 0.758647 1.930772 +L 0.000000 205 64.161484 64.552757 133.983597 116.738495 ++ 0.879226 4.167897 7.261358 19.703308 +L 0.000000 204 95.470161 39.634975 69.619270 107.987831 ++ 2.672341 3.416074 8.830455 18.071455 +L 0.000000 94 1.447367 72.036743 113.783005 89.082626 ++ 4.064810 4.044471 5.984875 11.430065 +L 0.000000 78 1.865354 69.055367 58.600273 81.072380 ++ 1.355587 1.997366 3.075839 6.875823 +L 0.000000 76 62.719486 71.735924 80.271080 81.333977 ++ 0.979289 1.780055 2.965509 6.540665 +L 0.000000 59 0.176032 51.527431 3.372638 70.107887 ++ 1.538591 2.173438 1.839187 3.389806 +L 0.000000 9 32.930145 81.339554 76.431839 65.253937 ++ 1.414256 0.311082 1.564924 0.996107 +L 0.000000 1 36.734104 61.811520 37.096832 64.654968 ++ 1.397529 2.088322 4.117707 7.407101 +S 0.000000 196 104.535675 3.043463 100.887169 77.856400 57.924084 111.755852 ++ 13.280718 10.772495 7.431593 9.852968 13.244255 7.485463 +S 0.000000 10 99.459114 39.706944 102.099709 68.095032 65.925156 100.103699 ++ 5.163695 4.287267 1.919487 3.373934 2.604921 3.028552 + +19 1 +50 10307 +38 12307 +12 10707 +3 12707 +6 22307 +4 2238d +67 11f03 +2 11b03 +2 11703 +1 11f23 +1 11d43 +4 10f03 +2 11f07 +1 11d07 +1 10627 +2 10317 +8 16307 +1 14307 +1 18307 + +G1 G +B 1.000000 201 3.155039 65.388573 69.906143 48.781979 65.739189 10.368743 127.904854 ++ 2.402769 2.106894 5.090991 17.412178 20.408934 8.509735 2.396605 +B 0.000000 70 98.817596 84.783295 3.150196 106.276611 11.234840 35.258343 32.714401 ++ 1.099743 3.264868 1.175280 5.107905 4.882269 4.298710 4.700566 +B 0.000000 68 121.673164 104.092865 45.409603 112.640350 7.603958 35.000187 41.084705 ++ 2.286614 3.432420 5.955954 3.624750 1.555457 2.907631 6.817499 +B 0.000000 58 31.638321 85.591133 131.801025 106.624352 7.604753 33.383568 27.084452 ++ 1.165284 6.159425 9.152044 3.533690 1.607331 2.967026 6.013548 +B 0.000000 2 48.385101 45.462772 121.264793 107.355331 6.065534 32.237602 16.488253 ++ 1.692780 3.653115 5.343567 4.130409 6.740546 4.170795 4.550116 +L 0.000000 9 65.380295 72.175674 125.699203 79.970131 ++ 1.535570 5.301487 12.156755 6.351846 +L 0.000000 5 126.902908 69.860405 4.083447 78.308800 ++ 1.369892 3.562361 1.595201 2.749864 +L 0.000000 5 28.471205 100.422447 37.550423 65.144913 ++ 1.294281 0.894858 0.703224 0.919594 +L 0.000000 4 33.974949 44.824013 68.147476 70.147186 ++ 0.905616 1.526078 3.626369 4.990133 +L 0.000000 3 61.603054 82.057838 59.878174 66.265457 ++ 0.248550 0.274646 0.601810 0.802332 +L 0.000000 1 72.644020 74.625763 27.390293 73.671394 ++ 1.070782 2.311886 3.736672 3.162754 +L 0.000000 1 21.596205 48.346550 93.977356 66.700989 ++ 1.070782 2.311886 3.736672 3.162754 +S 1.000000 201 45.260078 64.544365 43.624138 72.968933 58.365204 54.207195 ++ 5.498255 10.369446 12.351891 3.611915 4.347557 12.529223 + +19 1 +18 1009 +2 1049 +34 100d +30 1001 +29 1005 +6 1021 +2 1209 +1 120d +62 1003 +2 1013 +5 1083 +1 1041 +2 1101 +1 1403 +1 1109 +2 1045 +1 1825 +1 1121 +1 1025 + +H1 H +B 0.000000 198 96.412590 63.801083 35.308739 63.739048 40.785267 35.321903 121.755928 ++ 0.394981 2.670604 3.299278 5.002866 38.132866 5.394542 16.504023 +B 0.000000 183 32.463150 66.934761 105.091072 63.994980 32.304104 35.214775 121.191284 ++ 0.664328 2.795970 6.562027 4.865881 19.513763 5.097589 22.014385 +B 0.000000 125 63.790821 27.569605 68.868172 95.236519 4.855917 32.454262 98.683578 ++ 1.973171 4.486774 8.804080 10.250587 1.236695 1.606544 24.852764 +B 0.000000 124 127.649124 102.847214 67.931717 95.692833 4.967741 32.759823 99.167282 ++ 1.985641 4.821537 7.422298 10.791989 1.202401 1.649653 24.142128 +B 0.000000 15 32.498192 64.946915 89.408974 49.740730 96.517365 34.239952 67.438263 ++ 0.967742 0.990842 1.971600 2.967390 19.767395 2.809150 4.332727 +B 0.000000 7 96.324730 66.432022 2.158522 109.897263 7.387269 32.026718 33.879166 ++ 0.451413 18.655928 0.696371 5.596089 4.965043 2.179477 6.943583 +B 0.000000 3 32.435863 87.752075 136.858398 113.351486 4.735431 31.922161 28.720814 ++ 0.188414 3.270437 2.221512 3.856429 1.077827 1.471062 3.340962 +B 0.000000 2 32.017426 66.040588 115.289261 89.781311 30.906784 31.200253 30.089607 ++ 0.888467 5.384584 3.577845 5.454192 8.007089 2.809331 9.620093 +B 0.000000 2 32.686142 41.834709 137.164886 114.542282 4.711346 33.887077 27.931469 ++ 0.888467 5.384584 3.577845 5.454192 8.007089 2.809331 9.620093 +B 0.000000 1 32.188480 65.441200 115.513275 95.486526 24.736149 52.426640 37.314587 ++ 0.888467 5.384584 3.577845 5.454192 8.007089 2.809331 9.620093 +C 0.000000 3 65.009193 89.090492 30.256342 ++ 0.746414 0.550840 1.859532 +C 0.000000 3 65.905960 34.854500 36.710323 ++ 0.702741 1.047042 2.234225 +L 0.000000 197 95.481071 32.287575 67.922989 107.656006 ++ 2.202286 3.762518 10.715261 17.277712 +L 0.000000 196 31.437145 98.036591 67.420631 107.305534 ++ 2.754176 3.961678 7.272979 15.082175 +L 0.000000 58 64.859253 65.770607 82.130363 75.750053 ++ 1.360030 1.487346 2.216424 5.856421 +L 0.000000 57 1.140238 64.227295 59.893917 76.285522 ++ 1.530444 2.482161 2.363244 5.265131 +L 0.000000 1 125.981941 82.068138 4.471076 67.177208 ++ 1.679317 2.733816 3.853519 10.150455 +L 0.000000 1 101.096672 25.492315 108.230591 75.279045 ++ 1.679317 2.733816 3.853519 10.150455 +S 1.000000 201 71.232788 64.251358 62.628044 61.543720 63.652512 61.929913 ++ 10.779164 4.532114 6.043279 2.919323 4.093980 4.706204 + +23 1 +91 4300f +2 4200f +13 4301d +1 43e2c +1 43cac +1 53cac +2 4101d +50 4f003 +1 4d00f +4 47003 +1 4d00b +1 4f00b +17 43003 +3 4b003 +1 43007 +1 42007 +1 4f007 +4 4302f +1 4314f +2 4304f +1 4310f +1 4100f +1 6200f + +I1 I +B 0.000000 145 63.731911 50.608379 69.293427 88.539345 6.384907 32.967434 108.139603 ++ 1.875577 3.547762 5.741463 13.234223 3.100652 1.661539 25.397213 +B 0.000000 144 127.711418 79.949707 68.526306 89.478310 6.297720 32.807446 107.706009 ++ 2.260533 3.162815 14.373590 13.083604 3.022631 1.654224 24.793398 +B 0.000000 3 96.404533 68.510651 1.376882 111.968254 5.125764 34.443886 31.017952 ++ 0.300463 2.126298 0.974554 2.644329 0.980213 1.203509 3.850338 +B 0.000000 1 32.177345 59.219418 140.000000 109.282249 6.749842 35.888580 29.238388 ++ 1.353273 2.262140 4.549241 9.654052 2.367832 1.506424 10.965275 +A 0.000000 78 62.216644 54.121761 67.083206 ++ 3.864152 5.910217 2.270337 +A 0.000000 1 65.750832 105.742950 135.248840 ++ 3.864152 5.910217 2.270337 +L 0.000000 221 63.679565 65.302299 132.601944 110.572998 ++ 4.071386 3.839251 19.082695 14.901368 +L 0.000000 220 127.403717 65.148651 2.951462 110.426422 ++ 4.053876 1.882257 2.014740 14.843809 +L 0.000000 217 95.664886 57.148285 68.111755 109.177727 ++ 2.145334 2.995365 9.522577 16.647415 +L 0.000000 216 31.594912 73.328217 68.070778 109.012009 ++ 2.618171 2.947455 8.750565 14.859745 +L 0.000000 1 116.529991 63.374027 125.495941 115.766502 ++ 2.774549 2.067518 5.173566 13.593350 +L 0.000000 1 27.118732 69.521736 130.575958 88.908096 ++ 2.774549 2.067518 5.173566 13.593350 +L 0.000000 1 101.299072 50.659184 103.721306 73.821968 ++ 2.774549 2.067518 5.173566 13.593350 +S 0.000000 218 64.688828 65.169502 66.226593 64.528122 64.646881 66.438980 ++ 4.905847 14.731775 6.848619 2.388548 7.748479 9.255993 +S 0.000000 4 72.637054 29.809931 76.356125 66.352112 41.498940 76.632225 ++ 1.663736 9.392118 4.754256 1.520117 3.072491 5.958482 + +17 1 +131 23c3 +2 22c3 +69 23d0 +1 2350 +1 21c2 +1 22d1 +1 23d1 +1 21d2 +4 43d0 +1 22c1 +1 2390 +1 23c7 +4 21c3 +1 2347 +1 32c7 +1 23cb +1 2fe3 + +J1 Jj +B 1.000000 200 56.069103 60.115685 60.757832 80.257034 17.110163 62.074753 115.375275 ++ 3.856066 5.081961 8.463440 5.998817 8.823739 12.296677 14.521312 +B 0.000000 119 123.463753 86.187920 86.707657 109.335449 6.507830 36.183376 69.433945 ++ 2.098660 2.979004 8.111422 6.490946 2.802080 4.111717 14.210709 +B 0.000000 3 32.603413 65.158775 130.431381 107.178894 7.572381 34.670113 22.436266 ++ 0.154477 4.386211 8.352679 1.667101 2.897305 3.235367 3.603629 +L 0.000000 197 95.177612 69.152847 72.785500 100.396828 ++ 2.231338 6.347124 7.609317 13.992295 +L 0.000000 197 30.681868 84.195541 67.763878 100.533134 ++ 2.349552 4.525193 6.143397 10.344693 +L 0.000000 118 64.327988 70.580315 132.648575 91.138275 ++ 2.200659 3.635358 7.906227 9.169740 +L 0.000000 26 127.089302 61.369274 2.329300 74.028656 ++ 3.134659 2.854444 2.041913 6.503720 +S 0.000000 80 1.319654 127.829491 127.967506 53.124184 74.577415 127.985962 ++ 4.851962 4.328771 0.718627 3.015551 7.120522 0.310444 +S 0.000000 60 58.294743 124.197296 103.307671 63.220585 58.429710 121.677696 ++ 6.632097 7.057037 3.822524 2.163220 4.322543 5.753354 +S 0.000000 34 52.138687 94.018448 122.215286 75.495003 44.306946 117.486565 ++ 5.520212 5.310378 4.757266 4.583292 3.811070 8.627700 +S 0.000000 16 60.834652 80.444954 90.028534 66.221779 59.485443 94.981796 ++ 5.649368 16.733793 2.851010 2.589207 5.186578 4.437696 +S 0.000000 6 57.009125 88.496002 74.043129 77.269638 48.355637 62.753590 ++ 1.950110 4.355215 4.734673 3.554889 2.176146 6.203824 +S 0.000000 4 66.233742 83.209351 99.891769 65.376205 56.972855 111.875519 ++ 2.415316 11.959734 3.173860 1.443104 2.411601 3.177782 + +16 1 +31 23b +5 83b +1 21f +2 233 +1 87b +65 99 +15 d9 +46 13b +8 17b +1 11f +3 12b +1 13f +4 103b +15 43b +1 47b +1 171 + +K1 K +B 1.000000 202 0.922808 90.205666 69.819870 98.538910 17.831490 37.196934 124.547310 ++ 1.809845 3.615227 4.939116 3.566799 2.612902 2.589558 15.039881 +B 1.000000 202 32.512516 61.288742 112.719391 81.117508 39.380966 72.201408 82.967949 ++ 1.176430 5.791445 5.365693 19.616121 15.490292 21.233660 26.971098 +B 1.000000 202 96.317924 60.322105 26.303289 77.491112 41.068104 9.409250 100.920959 ++ 0.483165 3.180555 3.167563 5.877237 13.628953 10.229129 20.309250 +B 0.000000 125 63.665829 28.101809 68.789024 93.169586 5.165390 32.483521 101.146713 ++ 1.968598 5.501067 10.474546 11.474735 1.313885 1.389120 25.193336 +B 0.000000 2 96.744781 89.227005 0.726551 114.291435 4.341167 33.829990 30.058640 ++ 1.235603 4.130129 4.883508 7.140604 6.180622 8.860367 15.217919 +B 0.000000 2 32.127388 64.021317 139.743729 114.883156 4.562090 33.929520 29.344612 ++ 1.235603 5.947386 4.883508 7.140604 6.180622 8.860367 15.217919 +B 0.000000 2 96.512207 41.266251 3.322836 112.998741 5.040458 33.625957 31.082695 ++ 1.235603 4.130129 4.883508 7.140604 6.180622 8.860367 15.217919 +C 0.000000 20 62.941532 91.768616 20.139555 ++ 0.835638 2.205178 2.175555 +L 0.000000 199 95.557297 33.250511 68.264366 108.306381 ++ 2.166877 4.827687 5.130848 14.162503 +L 0.000000 139 44.556602 84.226784 43.758373 88.209618 ++ 2.615451 3.650286 5.627411 9.351332 +L 0.000000 112 108.925652 72.490486 29.813316 73.151039 ++ 1.461333 5.197793 4.018307 4.710346 +L 0.000000 43 80.606422 67.258804 108.121254 79.152534 ++ 1.421365 2.335194 2.929753 19.728493 +L 0.000000 33 14.459178 81.462906 108.963364 74.568954 ++ 1.150234 3.765849 4.505030 4.946226 +L 0.000000 3 62.714447 51.395077 119.426331 68.572258 ++ 0.975884 0.699995 1.595845 1.025290 +L 0.000000 1 94.160393 31.337336 95.567833 72.494217 ++ 1.551782 3.167021 3.464389 8.439351 +S 1.000000 202 63.351295 36.179790 55.685787 66.728104 48.215099 59.414413 ++ 11.263636 16.140781 8.776192 2.130862 10.287426 13.439376 + +29 1 +22 870f +24 830f +4 850f +7 818f +4 838f +3 878f +2 858f +1 a18f +2 a38f +1 848f +38 8f07 +6 8507 +27 8707 +1 8e0f +2 8107 +1 8307 +1 c407 +1 8b07 +1 8d07 +5 950f +13 910f +13 930f +16 810f +1 9b0f +1 832f +1 816f +2 811f +1 934f +1 8d0f + +L1 L +B 1.000000 203 10.503797 71.129082 60.208942 90.260048 21.377247 14.802641 127.779961 ++ 1.639296 4.071436 4.173936 6.873081 4.095449 7.333723 4.147608 +B 0.000000 127 63.697701 34.828148 68.765869 93.654839 5.412713 32.473740 101.435722 ++ 2.028054 3.993610 7.714600 12.496235 1.814422 1.802564 22.179710 +B 0.000000 4 96.648392 67.031151 1.590984 113.340179 3.374545 31.140820 39.300346 ++ 0.269735 10.155424 0.878007 5.209890 1.325606 0.815593 10.686944 +B 0.000000 1 32.704029 43.930107 139.190643 110.040901 4.629250 34.925438 25.830778 ++ 1.312362 6.073490 3.826926 7.498833 2.411826 3.317293 9.159514 +L 0.000000 202 0.129830 65.269577 1.464747 113.419167 ++ 0.878898 4.286120 1.831553 14.969489 +L 0.000000 199 95.618576 39.716553 68.256912 107.391014 ++ 2.217308 4.407716 9.910080 14.485023 +L 0.000000 198 31.798206 56.673519 71.846214 102.373215 ++ 3.117137 3.510159 8.276672 12.995839 +L 0.000000 83 63.415001 75.060165 20.792707 89.207458 ++ 1.624740 3.956342 2.266922 9.334229 +L 0.000000 52 64.285347 51.086418 128.765533 69.530365 ++ 1.354086 3.219578 9.081452 3.258969 +S 0.000000 201 21.435600 0.662108 16.575356 51.219257 68.247253 1.532655 ++ 13.314156 2.321880 7.227098 7.665976 19.659939 5.748249 +S 0.000000 2 17.397543 12.913129 15.317608 63.540520 79.012192 15.930958 ++ 13.314156 0.933111 7.227098 7.665976 19.659939 3.326533 + +17 1 +40 373 +2 3f3 +1 367 +3 333 +64 273 +8 2f3 +2 353 +2 573 +69 2f1 +4 271 +1 2b1 +1 2d1 +1 2d3 +1 2f5 +1 27b +2 377 +1 233 + +M1 M +B 1.000000 207 96.402687 81.319336 37.982403 72.308357 87.515556 102.453293 93.182587 ++ 0.592211 3.699741 6.852403 4.016215 16.957676 25.838169 22.669588 +B 1.000000 207 32.380962 66.814651 102.800270 79.979500 52.018478 41.313801 116.010216 ++ 0.496791 2.248808 6.332316 2.751557 10.137780 12.572024 20.575209 +B 1.000000 207 96.360397 43.599045 37.794094 73.260925 82.143127 10.562322 91.939491 ++ 0.524336 5.963651 6.763668 2.838024 15.540998 16.181522 24.561419 +B 0.000000 130 63.383480 20.458080 69.141510 95.791496 4.955610 32.860500 97.641418 ++ 1.946652 7.623501 7.155935 10.589010 1.371004 1.977043 21.645630 +B 0.000000 129 127.713455 110.517174 67.979668 94.910164 4.947242 32.289989 100.119858 ++ 2.000744 8.208732 9.101014 11.033073 1.130194 1.442399 29.877314 +B 0.000000 3 96.574265 103.453766 1.225816 112.620819 4.787362 34.498066 28.182579 ++ 0.268359 3.022474 0.462000 3.725561 0.450635 1.405447 4.407545 +B 0.000000 1 96.805321 27.080820 0.901076 109.860420 6.206563 35.566555 25.493589 ++ 0.926792 5.127818 5.647745 4.733867 7.496834 9.902767 16.942312 +C 0.000000 1 45.872948 65.978081 6.937196 ++ 2.000000 2.000000 4.000000 +L 0.000000 204 95.322906 24.946621 68.564819 108.006424 ++ 2.223368 6.098341 11.056107 17.472424 +L 0.000000 202 31.442953 105.577644 66.909714 107.963974 ++ 3.340013 6.434025 8.734090 16.792114 +L 0.000000 169 39.211292 53.486229 89.936989 93.446640 ++ 2.660688 3.128756 6.793083 9.697290 +L 0.000000 168 94.973091 90.947632 47.051918 75.057327 ++ 2.717964 3.786453 6.850090 6.150179 +L 0.000000 165 87.526741 78.502441 91.743042 96.988144 ++ 2.108618 3.640857 6.318028 11.280887 +L 0.000000 165 23.319162 78.376518 44.224972 89.898766 ++ 1.923135 2.376561 5.105190 9.167065 +L 0.000000 157 103.286934 47.735264 45.837780 85.363434 ++ 2.851790 3.372018 7.061068 11.853833 +L 0.000000 151 31.028793 35.213253 48.018692 75.988487 ++ 2.529224 6.219945 8.412397 5.817601 +S 0.000000 206 65.477654 64.199188 61.920620 62.159191 67.541016 62.915169 ++ 7.368952 5.417107 3.707662 2.715301 4.486103 5.705567 +S 0.000000 1 59.958992 85.682816 52.622051 61.777477 73.098694 48.037025 ++ 6.140793 4.514256 3.707662 2.715301 4.486103 3.962199 + +34 1 +13 18b1f +1 1831f +1 18a1f +3 10b1f +1 1cf1f +1 18f1f +19 1031f +1 1011f +56 1ff07 +6 17f07 +1 1ff0f +1 1d707 +6 13f07 +1 15f07 +1 13d17 +1 17f0f +1 1b707 +1 13707 +1 13e8f +1 17f17 +1 1bf07 +2 1f707 +56 1ff1f +11 17f1f +2 1ff3f +1 17f3f +1 1ef1f +1 1ef5f +1 1fd1f +3 1771f +7 1f71f +2 1f51f +1 1f61f +1 2ff1f + +N1 N +B 1.000000 206 96.405304 57.759102 35.220852 77.809174 52.917107 2.817539 118.367554 ++ 0.674943 4.078711 8.961642 5.126726 29.183546 6.220316 17.471992 +B 1.000000 206 32.380764 74.049255 101.694054 77.196594 54.128292 2.832371 117.095528 ++ 0.537282 2.607702 7.902534 4.452274 28.994640 8.368559 17.092716 +B 0.000000 126 124.660713 101.064873 81.977806 111.731392 4.601391 34.769215 70.975861 ++ 1.864745 3.861554 9.901540 4.759022 1.207017 1.544035 14.257932 +B 0.000000 126 63.704712 27.304585 69.610748 95.341461 5.061816 32.782341 100.480026 ++ 1.720401 4.073384 6.492799 10.095707 1.019425 1.532166 16.881691 +B 0.000000 1 125.752914 104.314590 102.580116 82.278214 3.968675 41.814995 34.274128 ++ 1.176956 3.485391 6.958188 4.253951 4.970026 3.949632 14.047361 +L 0.000000 205 30.890526 97.570198 61.788120 116.020790 ++ 1.946277 3.091000 11.282987 16.546560 +L 0.000000 204 95.383606 32.267727 66.903671 106.530777 ++ 2.576127 3.427806 9.452257 17.149954 +L 0.000000 204 108.523605 68.013168 44.852917 106.484657 ++ 1.957334 4.978343 10.567242 18.243019 +L 0.000000 199 44.213978 64.079834 92.549324 100.094383 ++ 2.311935 4.858807 7.980399 16.930664 +L 0.000000 168 31.054127 43.415668 50.325954 80.884277 ++ 2.337368 4.551823 7.933669 7.802095 +L 0.000000 148 94.840393 86.706856 88.955368 80.318970 ++ 2.748110 4.279626 6.535573 9.019087 +L 0.000000 1 101.660728 23.408854 106.180687 70.899933 ++ 2.177079 3.427147 6.742916 11.666628 +S 0.000000 163 71.654655 60.160355 44.650108 67.617653 61.985069 40.178410 ++ 9.173604 6.065896 9.329595 3.875578 4.146365 8.388556 +S 0.000000 43 71.456413 53.055115 65.674980 67.431015 63.067131 69.911819 ++ 5.551356 4.077484 3.166984 2.374385 3.599844 5.996542 + +20 1 +19 27ef +4 23ef +19 21ef +1 216f +66 17e3 +6 15e3 +1 17e7 +4 13e3 +3 11e3 +41 17ef +1 17fb +4 16ef +1 176f +3 12ef +21 13ef +1 11ef +8 15ef +1 1faf +1 13cf +1 13af + +O1 Oo +B 0.000000 1 80.855591 33.755211 23.499428 106.341064 6.405560 34.770401 15.527055 ++ 2.000000 4.000000 4.000000 20.000000 20.000000 20.000000 8.000000 +C 1.000000 208 65.297829 68.971672 103.309731 ++ 1.082438 6.024603 13.005810 +L 0.000000 3 96.955147 23.365103 75.354973 68.787613 ++ 1.893455 0.756982 3.720397 1.301407 +L 0.000000 2 126.582230 63.482170 3.464843 64.951241 ++ 1.893455 0.756982 3.720397 1.301407 +L 0.000000 1 33.774731 109.490723 81.145309 67.383995 ++ 1.893455 0.756982 3.720397 1.301407 +S 0.000000 200 64.529205 64.644661 64.080574 64.584656 64.659744 63.002102 ++ 2.516138 2.448725 3.039648 1.750610 1.738716 3.385557 +S 0.000000 5 65.449142 63.788948 37.967308 65.445114 63.873741 35.403297 ++ 1.897149 2.195265 3.701819 1.574273 1.579138 2.549920 +S 0.000000 2 65.389008 62.854656 49.841103 65.260483 63.192635 49.046978 ++ 1.996965 2.321995 2.730437 1.516557 1.658927 2.254577 +S 0.000000 1 64.582314 65.979256 90.841286 66.017258 65.999939 90.134064 ++ 1.996965 2.321995 2.730437 1.516557 1.658927 2.254577 + +8 1 +193 22 +1 23 +2 2a +5 42 +1 102 +2 82 +3 26 +1 32 + +P1 Pp +B 0.000000 200 115.200249 66.196686 40.791283 95.455109 20.757116 25.623564 91.694710 ++ 3.092536 5.491959 6.147311 13.474126 15.915850 11.257828 18.192921 +B 0.000000 121 63.865883 36.749531 68.927856 93.114891 5.320520 32.250664 101.655235 ++ 2.045873 2.569396 6.179256 9.437428 1.494004 1.681998 10.968609 +B 0.000000 2 96.535538 54.427124 2.476098 114.898590 4.129652 34.252285 29.338924 ++ 2.569204 3.816561 6.163284 9.397229 4.584735 6.329746 13.064688 +B 0.000000 2 64.387505 36.544682 72.953217 123.774185 1.844595 32.418621 44.549831 ++ 2.569204 3.816561 6.163284 9.397229 4.584735 6.329746 13.064688 +B 0.000000 1 32.475121 63.864410 140.000000 108.141068 5.085681 29.486134 25.486736 ++ 2.569204 3.816561 6.163284 9.397229 4.584735 6.329746 13.064688 +B 0.000000 1 117.132065 65.375252 83.863075 62.452129 48.075977 128.000000 128.000000 ++ 2.569204 3.816561 6.163284 9.397229 4.584735 6.329746 13.064688 +C 0.000000 200 68.858208 96.705765 48.065876 ++ 4.079711 9.242568 6.039643 +L 0.000000 198 63.431522 59.975327 133.766434 94.087807 ++ 2.743745 9.865398 7.747847 11.079084 +L 0.000000 196 95.612350 39.568039 70.326042 109.172592 ++ 2.144424 3.774794 7.342549 14.160185 +L 0.000000 117 4.534685 70.760124 61.421600 79.133743 ++ 2.033498 4.549158 8.352631 9.104604 +L 0.000000 68 0.528305 52.411335 3.678777 70.228943 ++ 1.647998 2.476103 2.542956 3.577323 +L 0.000000 1 100.820755 36.143684 104.691689 73.056450 ++ 1.647413 3.525989 6.190556 9.480299 +L 0.000000 1 32.820057 52.193089 70.155869 91.961540 ++ 1.647413 3.525989 6.190556 9.480299 +S 1.000000 201 123.857620 35.958355 115.134850 71.260078 62.260490 101.520233 ++ 13.461586 11.194516 10.808880 8.372733 8.929010 10.689560 + +15 1 +44 25c3 +29 21c3 +17 27c3 +2 26c3 +2 2543 +2 24c3 +73 23c1 +2 23c9 +4 21c1 +1 2351 +21 23c3 +1 21c7 +1 23c7 +1 2cc3 +1 31a2 + +Q1 Q +B 0.000000 196 126.671883 93.581284 12.375675 92.649178 28.818764 19.739758 58.070087 ++ 1.687200 7.992828 12.892457 14.641164 21.636248 13.055047 18.774822 +B 0.000000 132 94.698166 69.051239 -11.293993 110.588852 7.042349 33.841473 40.853527 ++ 1.918222 14.019607 14.157640 4.397501 2.064634 2.347951 6.590279 +B 0.000000 43 70.711540 42.013027 7.107338 96.693962 18.411970 48.942688 53.683975 ++ 2.430771 5.053236 3.163735 6.407182 5.699237 7.286918 9.593804 +B 0.000000 18 64.295982 27.475599 2.388128 98.458420 18.064516 36.109425 73.603661 ++ 0.363283 1.089276 1.770146 2.799878 2.149716 3.944280 4.021183 +B 0.000000 15 81.526459 48.374252 -1.619545 104.356018 6.663435 33.436306 23.239080 ++ 1.044949 3.620834 2.093310 4.535473 1.460323 1.899997 2.725607 +C 1.000000 201 65.245903 69.966576 100.852524 ++ 1.203003 6.316038 13.214554 +L 0.000000 70 123.106819 70.833733 -5.514468 84.258553 ++ 3.415844 10.655759 12.976923 13.569614 +L 0.000000 1 29.857773 102.539803 64.312744 86.795532 ++ 3.415844 7.399832 12.976923 13.569614 +L 0.000000 1 58.741367 74.828156 127.251251 64.539932 ++ 3.415844 7.399832 12.976923 13.569614 +L 0.000000 1 91.018692 30.876600 92.555710 66.548737 ++ 3.415844 7.399832 12.976923 13.569614 +S 0.000000 182 78.505989 68.385262 57.236965 68.482170 65.875145 54.620438 ++ 12.876821 4.388086 4.961297 4.977904 2.622931 4.405883 +S 0.000000 12 81.264030 60.912075 92.504784 64.671516 64.346878 90.119049 ++ 2.855279 3.230215 3.562971 1.632845 2.083990 4.136690 +S 0.000000 7 80.283073 63.492565 74.451668 69.693512 62.705688 68.081688 ++ 2.062692 2.639199 2.153079 2.974016 1.511574 2.696329 + +21 1 +36 427 +4 467 +50 423 +46 461 +10 463 +1 561 +1 623 +4 1023 +1 1063 +4 422 +1 462 +1 4a3 +5 471 +6 421 +10 431 +1 86b +6 42b +9 82b +2 827 +2 102b +1 465 + +R1 R +B 0.000000 202 96.382858 63.248280 30.925798 73.341545 38.717113 13.209373 104.708977 ++ 0.438597 1.894194 3.475022 3.868474 19.042379 15.616225 21.069502 +B 0.000000 202 2.334869 93.441170 54.335045 102.427177 12.207522 37.752010 78.188232 ++ 2.451605 2.843181 7.641466 8.599174 4.956017 3.848588 20.629154 +B 0.000000 128 63.760597 30.401415 69.888100 93.370331 5.136676 32.406567 100.031631 ++ 1.950062 4.962067 8.571724 12.144820 1.504801 2.541912 24.440317 +B 0.000000 3 95.855209 62.710384 66.237068 61.991150 124.077507 29.368160 128.000000 ++ 0.553698 0.581907 0.512077 0.432998 5.547243 2.134044 0.001000 +B 0.000000 1 32.200596 50.129978 120.666946 109.501709 4.110616 30.005169 29.369476 ++ 1.348491 2.570337 4.374523 5.755332 5.694289 5.767467 13.960839 +B 0.000000 1 96.060074 39.282505 2.907217 112.357994 3.822078 32.533020 34.198513 ++ 1.348491 2.570337 4.374523 5.755332 5.694289 5.767467 13.960839 +C 0.000000 202 65.537636 97.869759 49.826363 ++ 4.610379 8.105873 6.247357 +A 0.000000 2 32.552097 128.000000 -15.569940 ++ 8.000000 8.000000 4.000000 +L 0.000000 202 95.527298 34.917274 70.304245 107.931160 ++ 2.113482 4.347855 9.992728 17.326302 +L 0.000000 184 63.602734 56.635418 134.008179 86.679901 ++ 0.989322 4.433349 7.585451 10.695612 +L 0.000000 67 109.876114 71.909081 31.529078 73.498642 ++ 1.383192 3.089033 5.268137 6.566919 +L 0.000000 35 35.531555 93.309456 42.267723 75.003815 ++ 3.593707 2.059659 9.005785 16.402670 +L 0.000000 4 1.593768 61.965149 55.991592 68.934769 ++ 2.062087 1.370405 1.100420 1.486065 +L 0.000000 3 32.827698 50.870155 71.303741 113.654366 ++ 0.372427 1.634847 2.522250 2.856658 +L 0.000000 2 64.904228 44.743938 -15.932044 128.000000 ++ 1.681929 3.940874 4.944945 7.905763 +L 0.000000 2 0.326696 44.730202 -15.507660 128.000000 ++ 1.681929 3.940874 4.944945 7.905763 +L 0.000000 1 27.543579 89.028976 85.439018 68.420074 ++ 1.681929 2.736718 4.944945 7.905763 +S 1.000000 205 95.558525 52.339520 61.621346 65.949333 59.353848 58.353600 ++ 16.830473 14.095015 4.965788 5.422380 12.300901 7.372688 + +22 1 +1 20557 +50 20747 +7 20547 +46 20347 +11 20147 +1 20647 +44 20343 +26 20b43 +1 20a47 +1 20b47 +3 20b41 +3 21343 +1 21143 +1 20767 +1 20247 +1 2c3c7 +1 2c7c7 +1 30747 +1 22d0e +1 22f0e +2 20f47 +1 2270e + +S1 Ss +B 0.000000 202 1.188716 71.936638 97.869438 69.783844 45.163380 113.178917 106.203384 ++ 1.860239 10.965161 19.036720 15.682381 14.261122 44.025043 38.340115 +B 0.000000 196 62.884800 59.027657 43.401836 68.146942 54.192783 104.615128 118.819527 ++ 5.004175 6.430125 4.433028 10.304248 18.661673 24.606012 36.533443 +B 0.000000 51 96.242401 47.988190 1.467312 106.276489 6.955690 33.869240 24.496185 ++ 0.885401 3.740575 1.187112 5.260326 2.928065 2.384933 5.188639 +B 0.000000 37 32.451897 80.351814 130.594620 107.641464 6.945059 33.792454 24.294846 ++ 0.897735 5.638843 9.043820 6.097647 2.253736 1.708581 4.474109 +B 0.000000 6 62.655930 39.097492 59.717915 92.445625 24.882990 48.052345 38.820137 ++ 0.230754 1.031670 1.128824 2.306403 2.249761 3.511749 3.612220 +C 0.000000 7 64.864426 36.672012 38.464634 ++ 0.751268 1.547131 1.428359 +C 0.000000 5 67.897339 82.793564 35.601429 ++ 0.979596 2.106680 0.719119 +A 0.000000 2 120.591827 96.577530 35.977310 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 109.674515 110.916519 83.493172 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 32.596500 104.885567 -27.085205 ++ 8.000000 8.000000 4.000000 +L 0.000000 165 54.216469 71.710327 82.085159 87.442589 ++ 4.603840 3.575758 8.115684 17.896193 +L 0.000000 163 118.279327 60.748459 62.150974 90.747322 ++ 3.674863 3.776781 7.177567 17.688887 +L 0.000000 56 63.518280 71.638771 132.499496 81.064018 ++ 1.855175 4.607435 10.184869 7.046924 +L 0.000000 49 126.866806 60.193394 2.431904 84.423607 ++ 3.470381 3.912618 3.041456 10.604158 +L 0.000000 6 64.729591 65.996353 20.502205 65.443459 ++ 4.910754 4.763772 2.771997 0.854918 +L 0.000000 4 58.923359 57.870079 38.514221 103.553802 ++ 4.039128 5.258115 10.315651 11.278562 +L 0.000000 4 118.685860 73.994278 125.471031 70.084534 ++ 1.745782 4.172299 4.163522 4.073532 +L 0.000000 2 103.805222 51.608109 40.969109 92.008751 ++ 3.210317 4.095630 6.538678 9.920453 +L 0.000000 1 23.014330 75.161858 -27.679291 128.000000 ++ 3.210317 4.095630 6.538678 9.920453 +L 0.000000 1 87.448387 74.562881 -27.372467 128.000000 ++ 3.210317 4.095630 6.538678 9.920453 +S 1.000000 204 61.990479 67.645592 40.799133 62.984558 66.008972 54.474781 ++ 9.033048 5.727123 11.791913 3.398157 4.192152 13.478928 + +55 1 +4 10080f +6 101c07 +5 100c0f +5 10040f +6 100c0b +2 10040b +3 101807 +14 102c03 +3 101407 +1 10080b +1 101035 +1 100435 +3 101075 +2 102c0b +4 103403 +1 103843 +1 101047 +1 10000f +1 128080 +1 128d80 +1 103031 +2 103803 +1 10002f +60 100c03 +7 100403 +12 100803 +1 100003 +2 104c03 +1 102803 +1 106c03 +3 102c07 +5 100c07 +1 10280f +1 103c07 +9 101c03 +2 102c0f +1 105c03 +1 113c07 +1 100807 +1 103c0b +2 101c0b +7 103c03 +3 101403 +1 100407 +2 10200b +1 104c0f +1 1c1e0b +1 101007 +2 102403 +1 110c03 +1 113c03 +1 109c03 +1 10ac03 +1 107c03 +1 111c03 + +T1 T +B 1.000000 211 69.617630 46.055031 76.093651 87.114655 15.299176 11.813261 119.700821 ++ 1.807599 3.384090 10.559005 11.415777 6.903582 9.715784 12.700701 +B 1.000000 211 121.654396 80.482437 75.822304 89.451454 13.969547 55.954903 114.442856 ++ 1.667965 4.280772 9.532109 10.772770 8.690506 20.627390 14.893758 +B 0.000000 4 32.377087 69.336014 127.240318 107.874916 2.985333 32.260117 38.454193 ++ 0.176061 3.188700 9.667073 9.493845 1.296774 0.910310 6.085696 +B 0.000000 2 32.080910 53.852470 130.361618 108.274780 4.634187 30.363949 37.261097 ++ 1.217209 3.617854 8.803223 10.560798 3.427317 9.271862 10.521124 +B 0.000000 2 96.589897 66.371590 0.243470 110.058937 4.587788 34.875168 31.177534 ++ 1.217209 3.617854 8.803223 10.560798 3.427317 9.271862 10.521124 +B 0.000000 1 0.817821 101.040337 95.739349 117.467468 4.943347 33.333427 24.884239 ++ 1.217209 3.617854 8.803223 10.560798 3.427317 9.271862 10.521124 +L 0.000000 210 31.445425 71.660416 63.989929 104.255089 ++ 3.096652 4.692239 9.314801 10.696708 +L 0.000000 207 64.113808 63.806103 134.626328 116.605873 ++ 1.032931 7.458431 7.450264 16.641773 +L 0.000000 206 95.800949 54.967129 65.911362 104.015785 ++ 3.673573 3.229089 6.930004 12.303946 +L 0.000000 64 0.247939 64.618324 2.455063 73.858032 ++ 1.284828 1.957960 2.228759 4.426506 +S 0.000000 167 116.034904 60.776981 67.376373 75.842911 65.662827 68.617699 ++ 11.710878 10.297946 4.178986 10.422496 3.541834 7.810457 +S 0.000000 37 119.717003 0.604624 66.378723 83.005562 62.837440 125.012619 ++ 10.682659 0.276746 4.646001 5.411488 4.680458 5.876487 +S 0.000000 4 112.447289 0.437475 55.640350 76.631142 54.339638 91.711899 ++ 6.693209 0.262848 4.033526 1.213251 2.675933 5.207195 +S 0.000000 3 128.000000 18.744410 74.785072 88.717690 69.790504 99.954277 ++ 0.001000 8.884405 2.241919 2.790701 1.450672 4.439412 + +15 1 +56 7c3 +1 7e3 +3 6c3 +2 7c7 +1 783 +1 74f +98 5c3 +3 21c3 +1 547 +36 9c3 +1 943 +1 5d3 +4 11c3 +2 4c3 +1 55b + +U1 Uu +B 0.000000 195 32.448677 66.364861 76.812241 65.319199 61.786823 36.603638 127.987221 ++ 1.046082 2.918569 8.143518 1.125942 10.635363 10.725098 0.754035 +B 0.000000 122 123.954758 100.230377 89.547325 112.385368 5.194806 34.890137 64.549232 ++ 1.918142 4.886857 9.896957 4.007516 1.236522 2.929662 14.531374 +B 0.000000 113 67.029938 28.245663 91.160530 112.449623 5.175857 30.128643 61.219463 ++ 1.885147 5.309309 10.937681 5.511119 1.403028 2.809291 10.331465 +B 0.000000 8 32.255493 65.680229 115.280243 94.910904 22.307491 34.400269 28.936478 ++ 0.482476 2.421824 2.831276 6.082849 8.768800 3.301986 8.223076 +B 0.000000 1 32.863663 81.739517 116.355347 114.134514 5.832016 31.199417 20.941633 ++ 1.222784 3.783230 7.613045 4.181857 5.067788 3.447139 6.774806 +B 0.000000 1 62.481289 29.118490 113.835396 90.678879 9.018719 14.173676 39.993587 ++ 1.222784 3.783230 7.613045 4.181857 5.067788 3.447139 6.774806 +C 0.000000 8 66.510811 62.211639 65.727562 ++ 1.129926 1.378038 1.150475 +L 0.000000 200 30.495995 95.650536 72.650490 97.312927 ++ 1.995693 6.260341 8.151268 13.660302 +L 0.000000 196 96.379700 31.518175 76.339966 92.210297 ++ 2.834743 5.826471 11.328707 11.207279 +L 0.000000 186 31.833464 47.176308 79.052528 90.794518 ++ 3.209248 5.356290 11.481705 12.435221 +L 0.000000 181 94.926010 84.804573 79.267570 93.089584 ++ 2.486971 5.242095 8.823127 13.167201 +L 0.000000 5 127.774879 64.984131 2.132859 70.170563 ++ 1.219791 3.491782 1.596171 2.512160 +L 0.000000 2 63.299110 42.352188 138.685699 64.393387 ++ 1.975778 4.510735 7.893472 8.908222 +L 0.000000 1 68.646416 70.093498 25.693298 66.493210 ++ 1.975778 4.510735 7.893472 8.908222 +S 0.000000 180 60.520065 45.381889 61.589668 62.947083 63.044842 60.186810 ++ 15.243749 20.329092 7.214071 6.618123 7.308066 5.541662 +S 0.000000 20 71.211243 32.637840 61.104839 66.333717 29.038000 68.856628 ++ 5.443220 9.369777 4.881378 1.772218 2.808933 6.042761 +S 0.000000 3 57.873905 30.786051 48.147430 64.929733 68.922050 37.729652 ++ 4.832115 3.921777 5.343809 2.349290 3.035033 3.796375 + +29 1 +68 4787 +6 4387 +5 41ce +1 414e +1 41cc +9 4783 +1 41de +66 4781 +1 6781 +3 4581 +4 4f81 +3 4381 +1 4685 +1 4703 +1 4e81 +2 5787 +14 8787 +3 8687 +1 8487 +1 8187 +1 87a3 +3 4587 +1 4383 +1 4687 +1 10187 +1 10787 +1 4707 +1 10387 +1 4385 + +V1 Vv +B 0.000000 180 32.450447 66.586342 99.754372 78.491943 54.725708 43.428650 125.292145 ++ 0.579839 3.475478 7.812943 1.687337 5.699518 18.099239 5.444583 +B 0.000000 79 71.873894 35.799404 81.212799 114.696716 4.032866 30.716780 68.256714 ++ 3.294900 6.579791 9.204849 6.549133 1.120226 1.587764 19.083385 +B 0.000000 70 118.093575 89.282845 82.148529 116.234322 3.065981 33.498417 60.826054 ++ 2.014150 14.639586 14.547703 5.265099 1.269894 0.574979 16.929647 +B 0.000000 16 32.281704 66.613007 80.855583 74.491409 65.369644 26.082855 82.317337 ++ 0.869336 0.951522 2.391179 3.406343 14.125274 8.020597 5.637372 +B 0.000000 4 32.409000 60.175255 116.534698 102.989845 13.789852 30.935995 32.343460 ++ 0.117204 4.309233 1.323477 6.555231 8.016945 1.449196 8.566526 +B 0.000000 2 114.049721 95.542496 110.649597 120.845352 3.857045 33.068642 33.371498 ++ 1.375086 4.475202 6.167004 4.474324 5.968767 5.893430 9.401773 +B 0.000000 1 112.633362 46.402237 23.490421 111.242622 3.299136 34.014736 25.918737 ++ 1.375086 4.475202 6.167004 4.474324 5.968767 5.893430 9.401773 +B 0.000000 1 32.730854 37.993519 140.000000 116.982353 5.422155 32.685329 33.192520 ++ 1.375086 4.475202 6.167004 4.474324 5.968767 5.893430 9.401773 +C 0.000000 4 65.712410 81.743279 40.423134 ++ 0.872252 2.223712 1.859943 +L 0.000000 193 23.896536 83.214859 62.650642 125.317688 ++ 2.517157 6.048098 5.860932 19.467518 +L 0.000000 181 38.129688 54.730171 85.307846 88.641838 ++ 2.915381 7.618084 7.539616 9.778925 +L 0.000000 181 103.560555 43.785088 64.055779 122.321930 ++ 3.441795 4.181841 7.589634 12.854869 +L 0.000000 162 87.944252 75.009392 84.748772 89.518272 ++ 2.524493 6.432214 7.638472 12.262012 +L 0.000000 19 95.487999 29.150770 63.271736 120.519844 ++ 0.643591 3.119128 9.344585 19.435310 +L 0.000000 3 28.466835 93.366524 98.012070 69.024467 ++ 0.696869 1.949669 1.358367 3.930207 +L 0.000000 2 18.397514 57.992760 42.816742 93.387253 ++ 1.788177 4.132973 5.801989 8.727941 +L 0.000000 2 61.561584 48.041908 114.358177 66.184456 ++ 1.788177 4.132973 5.801989 8.727941 +S 0.000000 100 114.059296 42.328957 58.791412 84.958412 67.627495 59.627094 ++ 9.859082 20.651695 5.178342 2.492852 3.315331 8.175756 +S 0.000000 58 74.203865 56.638237 61.698948 70.589294 63.682888 63.294159 ++ 9.437443 10.833864 6.577875 3.100785 2.382781 7.149182 +S 0.000000 20 68.762260 18.561647 60.772446 68.573387 34.928360 96.997261 ++ 2.485662 8.088118 3.814926 1.367648 1.871849 6.707728 +S 0.000000 15 101.504585 0.524211 57.752987 83.747520 17.792648 122.407608 ++ 6.343030 0.336403 4.285561 2.201013 3.277636 4.946648 +S 0.000000 5 103.636642 0.450019 117.591408 82.430496 50.680161 126.602829 ++ 5.493546 0.284722 5.465314 1.221298 2.191226 1.738692 +S 0.000000 1 59.625607 78.953644 41.502132 66.070389 64.737923 37.941105 ++ 6.723752 7.677832 4.345968 2.076719 2.607764 5.232770 +S 0.000000 1 84.105667 79.459808 60.985065 75.009033 75.352974 50.305267 ++ 6.723752 7.677832 4.345968 2.076719 2.607764 5.232770 + +32 1 +11 21e03 +25 21e07 +16 21e01 +10 21e05 +1 30b12 +7 20a0a +1 20a0c +1 21e0e +3 20a0e +3 20a08 +1 20b16 +1 20b12 +1 20e0e +1 30b14 +56 41e01 +1 41a01 +20 81e01 +1 805c01 +1 45c01 +1 405c01 +8 20e07 +8 20e05 +2 203603 +7 103607 +1 20b427 +6 103603 +1 203467 +1 102603 +1 203407 +1 109c07 +1 20e01 +1 20e81 + +W1 Ww +B 1.000000 202 96.320259 61.519367 28.629677 77.875336 58.640560 41.586662 105.791039 ++ 0.697591 6.317729 6.367503 2.731264 14.325352 14.732762 21.810978 +B 0.000000 197 32.541073 47.171371 100.055931 73.890648 71.814621 42.263626 100.688889 ++ 0.935476 4.261868 8.312105 3.000686 14.918360 43.127045 28.009933 +B 0.000000 195 32.427525 86.431778 100.386520 75.772453 66.319069 36.932667 102.287231 ++ 0.565180 6.080570 10.373857 2.898170 10.253114 18.176300 29.418575 +B 0.000000 48 67.959457 22.935959 80.847130 113.885918 4.374584 30.586878 66.690704 ++ 1.850095 10.141331 8.474865 5.756567 1.130702 1.364421 16.752451 +B 0.000000 43 121.150322 100.522339 78.007790 116.726654 3.227468 33.497147 56.931488 ++ 2.953305 6.884431 9.272946 4.833237 0.924988 0.654195 8.603382 +B 0.000000 5 32.469700 64.490555 116.385170 96.242348 20.732803 30.668287 25.319330 ++ 0.349351 0.690141 2.024914 0.603404 1.939721 3.847011 4.718565 +B 0.000000 5 32.504696 49.421402 140.000000 112.548279 5.400838 33.905781 29.891809 ++ 0.301016 20.302383 0.001000 4.530951 1.158882 2.018806 3.581755 +C 0.000000 4 76.313965 83.840714 17.987429 ++ 1.073407 3.778325 1.047562 +C 0.000000 3 65.654442 90.690529 34.074097 ++ 0.518498 1.283760 1.098922 +C 0.000000 1 64.417397 57.321793 8.142881 ++ 0.795953 2.531043 1.073242 +C 0.000000 1 58.227470 92.280113 21.372824 ++ 0.795953 2.531043 1.073242 +L 1.000000 202 25.641335 99.884491 63.664566 124.218239 ++ 2.482559 6.232855 10.285339 22.101025 +L 0.000000 201 100.818092 25.556528 66.426041 119.430443 ++ 2.618532 6.405405 9.397027 16.144991 +L 0.000000 164 36.632069 36.904556 92.370506 76.372200 ++ 2.554851 6.063678 7.281353 6.651093 +L 0.000000 156 101.444038 71.681816 41.005917 78.581627 ++ 2.855622 4.144624 6.107867 10.069719 +L 0.000000 146 36.884853 77.978386 91.803154 77.748611 ++ 3.460312 5.010828 4.338405 8.129366 +L 0.000000 122 24.299231 52.901421 40.868122 85.738655 ++ 2.510130 4.391317 5.075523 8.594226 +L 0.000000 119 89.728996 92.401833 90.761833 81.349609 ++ 2.571187 6.073613 5.817420 9.944326 +L 0.000000 82 89.536476 50.421421 93.443649 84.020874 ++ 2.684904 4.868265 2.786913 7.912714 +L 0.000000 1 99.047676 30.295692 36.910961 71.930862 ++ 2.542624 5.212882 5.101822 8.737996 +S 0.000000 161 82.334145 59.952595 60.746361 75.952660 58.824348 61.371601 ++ 11.557420 6.732090 4.816116 6.137715 3.916366 6.498829 +S 0.000000 20 70.571274 39.098740 58.778049 68.336731 43.090202 43.804409 ++ 3.213827 4.415861 9.158152 1.322325 1.073952 3.361606 +S 0.000000 19 90.429802 17.133827 60.548012 81.656532 39.183262 45.084415 ++ 8.380417 7.893133 2.204453 2.199413 3.135913 2.564263 +S 0.000000 1 91.372894 58.695118 35.953331 72.298531 61.743057 48.233368 ++ 6.863660 5.973023 3.544810 3.219817 2.708744 3.593764 +S 0.000000 1 85.381233 15.369962 51.949780 80.177757 36.690598 32.402733 ++ 6.863660 5.973023 3.544810 3.219817 2.708744 3.593764 + +64 1 +1 107807 +4 10180f +1 12381f +2 105817 +1 10380f +2 123807 +1 121807 +2 12581f +1 125817 +1 10580f +1 13180f +1 107817 +2 127807 +1 123817 +1 12380f +1 127a1f +5 101817 +1 10189b +1 101cb9 +1 101931 +5 10181f +1 1018b9 +1 801929 +1 101929 +1 101893 +53 17f807 +1 16b807 +1 137807 +3 16f807 +1 15f807 +1 13d807 +6 11f807 +16 27f807 +1 2fe80f +3 27d807 +2 115807 +17 10f807 +7 10b807 +3 12b807 +1 11d807 +1 12781f +1 11b807 +1 11f817 +1 13d817 +5 117807 +5 117817 +1 12f807 +1 13f857 +1 11f847 +1 13f817 +6 41f80f +2 47f81f +3 43f81f +5 43f80f +1 46f80f +1 43784f +1 103f85f +1 42781f +2 10b80f +1 10f80f +2 12b817 +1 12f847 +1 139807 +1 17f80f + +X1 Xx +B 0.000000 199 63.441895 37.940659 68.935318 103.117088 14.436301 32.181629 123.740028 ++ 1.789178 4.035739 5.117345 2.793677 1.949163 2.134255 10.127218 +B 0.000000 199 0.089180 92.921532 67.394783 103.169769 14.178935 33.533218 122.749229 ++ 1.975453 3.494328 5.059707 2.970582 1.773047 2.113807 8.519472 +B 0.000000 187 96.397812 62.300308 20.449444 83.686348 34.661690 34.771366 93.436966 ++ 0.461770 3.661850 4.684320 12.634250 19.594866 6.131474 20.639027 +B 0.000000 183 32.400982 68.593391 117.813576 84.424309 33.636116 33.512074 85.885056 ++ 0.508115 4.005278 5.303451 11.791836 12.876834 12.583853 23.512835 +B 0.000000 16 32.354271 66.986031 95.786026 57.842247 94.001564 31.793812 47.023327 ++ 0.570469 0.789400 1.251089 2.091203 10.550331 5.351053 3.968829 +B 0.000000 12 96.475594 65.587105 22.971172 53.565063 88.438141 26.919016 50.086590 ++ 0.297102 0.630559 1.533975 3.210857 4.134339 2.311650 3.659693 +B 0.000000 4 32.550503 47.587280 140.000000 112.182022 4.637251 34.287346 29.299910 ++ 0.133696 6.390588 0.001000 3.883593 0.878287 1.726325 2.765863 +B 0.000000 3 96.587524 93.634201 1.329960 115.936272 3.917478 33.577332 32.058479 ++ 0.233041 5.359047 0.582836 3.743755 0.725922 1.198979 4.134332 +C 0.000000 4 67.060333 93.446625 20.044735 ++ 0.729285 1.366308 2.028282 +A 0.000000 2 32.303947 128.000000 -5.838327 ++ 8.000000 8.000000 4.000000 +L 0.000000 59 41.903446 87.559105 43.786972 78.733551 ++ 2.762954 3.017034 7.193882 7.652588 +L 0.000000 53 83.798347 41.932095 39.459366 78.268417 ++ 2.241021 2.857794 3.342522 5.724767 +L 0.000000 35 105.676865 44.946884 92.447273 73.785339 ++ 1.871354 3.811701 6.521281 5.954125 +L 0.000000 28 20.621510 86.846573 94.599884 73.576691 ++ 1.946466 2.544159 5.586980 3.672610 +L 0.000000 1 112.919037 63.410732 -0.976793 91.345085 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 80.440079 63.166965 4.284106 91.486778 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 16.266388 65.984360 -0.526348 91.471451 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 63.472546 40.974293 140.000000 64.551750 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 127.176292 85.868263 4.145823 64.740150 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 48.268940 65.859261 4.374274 90.604935 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 64.446236 112.522881 -13.582910 128.000000 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 0.219698 112.859238 -13.595899 128.000000 ++ 2.205449 2.898851 5.661166 5.751023 +S 1.000000 200 63.292152 65.324570 62.604851 64.836815 65.008881 56.278671 ++ 4.836371 5.967857 11.135469 2.609001 4.493628 14.247530 + +26 1 +49 40000f +26 40040f +15 40100f +13 40200f +3 40300f +5 400017 +8 400033 +2 40012b +1 402033 +1 401017 +1 40112b +1 40010f +1 400817 +11 40280f +13 40140f +26 40080f +14 400c0f +2 40048f +1 40108f +1 42100f +1 40004f +1 44044f +1 49c200 +1 40044f +1 400c4f +1 70060f + +Y1 Yy +B 1.000000 205 69.642204 44.362354 63.470695 107.761040 8.158669 36.385777 105.386139 ++ 1.944087 4.178293 5.145462 10.013327 2.299987 4.130992 20.309855 +B 1.000000 205 122.254509 83.570526 60.998512 109.816086 7.811713 28.845612 104.065407 ++ 2.276255 5.619423 4.450990 10.299380 2.865698 3.895854 22.030848 +B 1.000000 205 32.407879 67.747971 111.629692 82.892220 38.760551 34.896545 100.951614 ++ 0.576576 3.395314 7.782160 7.647441 10.610886 5.783079 19.418104 +B 0.000000 4 32.239555 39.297852 140.000000 110.609947 4.588174 34.273697 31.057976 ++ 0.130537 1.543749 0.001000 1.102964 0.855755 1.575650 2.135783 +B 0.000000 1 5.111273 31.708612 37.859783 100.861267 13.399154 41.863438 19.185705 ++ 1.207840 3.684195 4.344903 6.681599 3.715961 3.511942 15.164559 +B 0.000000 1 96.612068 69.309906 2.207775 110.792419 3.572656 34.998672 29.633627 ++ 1.207840 3.684195 4.344903 6.681599 3.715961 3.511942 15.164559 +A 0.000000 1 72.122482 102.937317 77.472351 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 19.532059 90.564034 40.514301 ++ 8.000000 8.000000 4.000000 +L 0.000000 194 20.926275 86.966171 92.315666 85.724640 ++ 1.560187 4.932343 8.280481 10.447074 +L 0.000000 158 106.607712 43.643135 89.463257 83.627846 ++ 1.639304 3.668783 12.240396 9.155657 +L 0.000000 55 0.144292 66.492981 1.721044 71.851555 ++ 1.174731 1.554222 2.314557 4.531511 +L 0.000000 18 44.292213 53.064194 107.077423 73.093681 ++ 0.718503 8.360185 10.839523 28.318903 +L 0.000000 5 64.271385 45.264233 138.601395 66.141144 ++ 1.599782 1.789857 1.322830 1.090261 +L 0.000000 4 84.349266 75.412689 106.930359 67.750488 ++ 0.232225 1.352329 1.390888 1.331707 +L 0.000000 2 26.563833 66.326111 48.809772 66.156342 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 44.742775 31.120823 33.533699 71.009621 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 86.552612 23.749001 39.955353 122.901054 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 91.984245 25.024860 87.531517 105.807121 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 64.520576 35.349819 40.268795 128.000000 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 122.055023 31.531044 64.108139 116.615288 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 5.863672 27.843702 23.871761 106.517067 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 0.910495 35.859016 39.998158 128.000000 ++ 1.154122 3.022600 4.899366 6.170154 +S 0.000000 165 109.663445 59.531147 59.564640 66.441628 66.236145 52.757542 ++ 14.548238 12.587041 6.080742 4.643904 3.317239 14.638562 +S 0.000000 27 123.792953 4.660630 65.228966 69.945267 49.310947 116.891533 ++ 8.144866 8.665133 10.199141 2.756315 5.408697 17.119234 +S 0.000000 11 116.154778 0.515648 97.009521 65.020500 74.044777 128.000000 ++ 6.048635 0.300945 3.991299 2.923981 2.788128 0.001000 +S 0.000000 2 128.000000 0.186755 87.411682 72.861542 59.111469 122.644302 ++ 9.128087 6.003696 4.659050 3.288271 3.537538 8.842640 + +24 1 +38 400707 +4 400607 +67 400307 +7 400507 +14 400b07 +3 402307 +4 800107 +17 800307 +1 2000107 +1 2002107 +20 400107 +1 40030f +3 400907 +5 401707 +1 40032f +1 40070f +3 800007 +1 1004207 +6 1000107 +3 1000307 +2 800207 +1 804007 +1 13f8bd7 +1 40010f + +Z1 Zz +B 1.000000 200 63.084858 48.834873 81.954590 80.173630 30.037321 2.037663 121.804665 ++ 1.541456 6.023522 8.591763 5.608208 14.620169 3.984088 17.590174 +B 1.000000 200 0.461409 81.400078 52.113541 80.955826 34.292923 1.505721 121.217918 ++ 1.610144 3.770284 4.619421 6.720671 29.929998 3.601678 23.062780 +L 0.000000 199 63.919987 66.813538 132.668976 108.181870 ++ 1.629956 5.667735 6.918536 17.587667 +L 0.000000 199 0.110300 63.858395 1.497825 114.500175 ++ 0.878822 8.190874 3.593548 20.979820 +L 0.000000 195 19.799486 76.338562 70.513649 114.482361 ++ 1.325688 5.075359 8.115180 17.536514 +L 0.000000 193 83.634224 52.382248 63.162643 112.925621 ++ 1.295875 7.253551 12.883206 17.756384 +L 0.000000 122 63.908829 74.997398 19.055262 78.085793 ++ 1.556447 3.641152 2.833400 8.157567 +L 0.000000 88 127.320686 55.233608 113.542488 77.076462 ++ 2.459943 3.204240 6.386688 6.577067 +S 1.000000 200 61.655777 63.180267 88.571419 63.233910 65.010651 93.291443 ++ 5.806026 10.251654 18.177458 3.218997 5.038483 13.527789 + +9 1 +40 17f +70 1ff +70 13f +6 1bf +1 13b +7 1df +4 1ef +1 1af +1 177 + +[1 [ +B 1.000000 200 127.611198 72.107880 48.972610 82.341957 4.927084 32.451332 123.392326 ++ 1.862434 1.751505 5.878459 8.199158 1.398745 1.529488 13.594073 +B 0.000000 2 64.641785 50.931305 57.752258 103.396561 3.023966 31.984627 48.406303 ++ 1.862434 1.751505 5.878459 8.199158 1.165621 1.529488 7.866940 +L 1.000000 200 63.630947 65.881218 133.393875 101.766197 ++ 3.108536 1.894703 6.844024 12.898200 +L 1.000000 200 127.451302 65.098953 -34.422504 106.991241 ++ 2.394147 1.702671 5.126449 12.778862 +L 0.000000 198 95.404762 51.587948 49.757099 119.429718 ++ 1.998755 2.332887 12.555111 23.489565 +L 0.000000 198 31.638351 65.884392 48.998062 110.482849 ++ 1.984798 2.455369 6.288793 11.418715 +L 0.000000 62 9.743550 71.213387 118.284866 84.325912 ++ 2.949486 1.664373 11.457412 9.058783 +L 0.000000 39 56.982235 71.154022 -21.856983 84.374893 ++ 3.364452 1.852523 6.095297 8.932294 +S 0.000000 199 63.657455 0.505349 67.945450 65.131561 46.592113 69.858322 ++ 6.822618 0.300706 7.764661 3.249407 8.257648 22.614334 +S 0.000000 1 62.602085 21.321474 72.759422 68.286728 46.460449 71.204674 ++ 5.685515 0.300706 5.392125 2.256532 8.257648 13.086998 + +9 1 +28 1fd +10 1bd +1 1ef +126 13d +31 17d +1 11d +1 23d +1 15d +1 16f + +\1 \ +A 1.000000 200 78.474701 39.531746 51.934616 ++ 4.832417 6.196556 10.415517 +L 1.000000 200 103.552940 60.345871 50.119671 126.585747 ++ 2.475607 2.403204 6.724149 6.526370 +L 1.000000 200 39.509335 70.616310 52.119446 126.589844 ++ 2.428753 3.636809 9.940395 11.444258 +S 0.000000 193 63.894230 64.188484 0.504943 64.043221 64.087914 0.522546 ++ 21.303850 19.159748 0.300117 4.195149 4.935612 0.283528 +S 0.000000 7 103.895172 43.120167 0.325872 74.802559 63.067837 0.551673 ++ 2.954525 4.076750 0.296662 1.045784 1.696193 0.301800 + +2 1 +193 f +7 17 + +]1 ] +B 1.000000 200 63.571487 58.438869 50.010590 82.105423 5.015509 32.826668 123.197601 ++ 1.831586 2.153426 5.975486 12.399308 1.307205 1.598902 16.412678 +B 0.000000 3 0.632815 80.905731 55.873093 119.362114 2.158618 32.216583 57.148651 ++ 0.078090 1.585083 0.548709 5.928654 0.488620 0.863059 2.771714 +B 0.000000 1 0.051752 81.704224 92.226685 109.788780 2.935578 31.006443 42.649334 ++ 0.954838 1.689802 3.262098 7.269642 0.788979 1.230980 5.343389 +L 1.000000 200 127.495132 64.398041 -35.019836 100.043663 ++ 2.340898 2.331560 4.630896 12.566772 +L 1.000000 200 63.799820 65.068436 132.923050 107.713341 ++ 2.782626 3.412839 7.088197 19.049490 +L 0.000000 199 95.781021 64.390892 50.673523 111.332504 ++ 2.070275 2.191806 14.099358 14.311509 +L 0.000000 199 31.389811 78.847275 48.716240 120.149818 ++ 1.983545 2.361078 14.268994 13.252702 +L 0.000000 59 73.682129 58.831570 -22.212595 84.364174 ++ 3.469853 1.651915 5.960519 9.398498 +L 0.000000 25 121.176521 59.518448 116.576599 81.762283 ++ 3.165910 1.083387 11.214920 10.334977 +S 0.000000 196 65.001595 128.000000 68.385521 63.115326 81.854958 71.098495 ++ 6.990565 0.001000 5.348503 2.232478 7.775255 19.202818 +S 0.000000 1 72.193336 119.028519 67.705666 65.353081 76.457939 65.269958 ++ 5.825471 0.001000 5.348503 2.232478 7.775255 13.335290 +S 0.000000 1 54.436668 113.778633 53.597797 63.864895 78.916130 56.460953 ++ 5.825471 0.001000 5.348503 2.232478 7.775255 13.335290 +S 0.000000 1 80.297157 112.095528 82.431488 65.094528 64.298180 79.842308 ++ 5.825471 0.001000 5.348503 2.232478 7.775255 13.335290 +S 0.000000 1 62.373657 97.719055 56.576065 66.716415 72.635315 58.615368 ++ 5.825471 0.001000 5.348503 2.232478 7.775255 13.335290 + +11 1 +16 3f9 +43 2f9 +9 379 +123 279 +1 259 +1 479 +1 879 +1 1079 +1 2079 +3 27b +1 23d + +^1 ^ +B 1.000000 200 96.288742 64.523209 97.434944 99.031334 17.026390 32.780235 79.931213 ++ 0.560395 1.619645 15.412834 3.704955 2.281463 2.671761 11.940050 +B 0.000000 7 48.562183 48.729557 120.303200 111.121208 6.495940 34.020660 19.745653 ++ 0.284446 3.596939 7.371547 8.523658 2.982918 2.262278 5.605773 +B 0.000000 6 15.875752 83.067459 126.878845 114.342468 4.942158 33.002056 25.707865 ++ 1.026152 2.373580 11.641595 5.901751 2.599442 1.981454 5.939803 +L 0.000000 194 46.071556 80.735176 116.009445 120.806015 ++ 1.991363 2.744026 15.252220 23.496756 +L 0.000000 191 82.905907 49.323208 116.646675 122.482086 ++ 1.830893 2.691353 15.620576 19.469791 +L 0.000000 135 113.744965 76.309982 101.476128 86.435844 ++ 2.127710 2.316786 15.198489 14.170253 +L 0.000000 100 14.184648 52.740379 100.840012 85.081924 ++ 2.734421 2.683494 13.168486 14.742293 +S 0.000000 199 59.671200 64.788048 59.520737 61.281151 64.352783 57.211735 ++ 22.060001 17.175133 13.427321 5.872659 8.933920 12.342134 +S 0.000000 1 77.948471 54.725662 23.082628 62.565048 68.908119 9.212225 ++ 15.319444 11.927175 9.324528 4.893882 5.170092 10.285110 + +18 1 +19 d9 +68 f9 +52 b9 +1 159 +41 99 +3 bd +3 e9 +1 d1 +1 f1 +1 91 +1 e1 +1 8b +2 eb +1 f7 +2 ab +1 db +1 f5 +1 fd + +_1 _ +B 0.000000 6 32.490204 54.072235 -35.788708 111.547096 3.192255 31.734289 38.487507 ++ 0.281225 10.108675 3.551733 3.867343 1.241193 1.287600 5.319109 +B 0.000000 3 96.392128 73.368576 -39.675045 109.221214 3.208603 30.933311 34.685753 ++ 0.102246 4.889478 0.141218 2.030238 0.269111 0.492629 2.357179 +A 0.000000 195 0.086997 54.120743 -38.950596 ++ 1.437120 7.899461 2.074569 +L 0.000000 199 0.139220 65.272530 -39.552589 118.211823 ++ 0.862009 7.757318 0.289954 17.920324 +L 0.000000 197 64.192719 65.529648 -35.418049 119.232140 ++ 0.707310 5.872394 4.215955 20.310246 +L 0.000000 163 28.771793 99.159393 -39.221249 99.354088 ++ 9.023366 5.756540 2.852888 15.541869 +L 0.000000 155 94.973297 31.733459 -38.271336 100.823166 ++ 7.622963 5.983656 2.769842 14.890718 +S 0.000000 178 63.640320 65.819046 52.990940 63.702057 64.843513 53.073822 ++ 31.793993 15.415807 16.545467 12.154965 4.764941 14.211832 +S 0.000000 17 2.606291 64.362709 64.431938 15.610415 64.497101 64.567711 ++ 1.340635 0.287867 0.337028 9.689609 0.276228 0.280435 +S 0.000000 3 32.026237 51.791603 8.587368 56.400043 61.855618 16.452736 ++ 1.080147 1.302049 1.015105 0.023165 1.199677 0.057894 +S 0.000000 1 64.788750 106.887772 64.549629 64.765282 75.957748 64.947723 ++ 8.166647 4.098445 5.046674 7.289246 1.594964 4.850054 +S 0.000000 1 63.612076 66.533333 0.048471 64.598442 66.137077 0.006695 ++ 8.166647 4.098445 5.046674 7.289246 1.594964 4.850054 + +14 1 +118 fc +22 bc +1 d6 +10 dc +17 17c +19 9c +2 fd +2 fa +2 89 +3 25c +1 cd +1 43c +1 f9 +1 81c + +`1 `" +B 0.000000 135 1.098677 71.787994 117.889961 107.268730 11.536766 33.725090 36.632938 ++ 3.372035 1.609314 3.458428 6.401232 5.830922 5.472333 8.273043 +A 0.000000 74 72.743584 84.195389 99.654274 ++ 11.945768 6.441480 13.801171 +L 0.000000 185 96.074287 58.687389 105.563431 95.194221 ++ 9.220844 3.114913 15.308997 20.992994 +L 0.000000 172 40.714180 71.475456 105.597748 94.349922 ++ 17.504314 5.113864 14.222587 18.715672 +L 0.000000 58 0.104070 65.995926 88.405586 87.262390 ++ 2.634854 1.712654 5.721299 17.135361 +L 0.000000 19 112.923378 63.326347 89.254639 84.976929 ++ 0.951610 1.589247 5.067267 9.915505 +L 0.000000 18 68.847733 64.875008 135.355423 79.724304 ++ 8.294603 2.287370 3.697350 13.242955 +L 0.000000 3 60.725952 60.283699 106.143303 71.064438 ++ 2.595556 0.411144 3.422511 6.026776 +L 0.000000 1 18.932302 70.982719 133.135925 67.459663 ++ 5.000737 1.771621 7.511596 14.338210 +L 0.000000 1 15.826595 68.230713 91.510361 91.212440 ++ 5.000737 1.771621 7.511596 14.338210 +L 0.000000 1 37.963005 77.118385 75.380638 110.614105 ++ 5.000737 1.771621 7.511596 14.338210 +S 0.000000 157 30.538834 55.315914 53.409931 60.064140 61.802975 58.960438 ++ 18.058334 22.452402 25.588451 4.067739 14.476027 33.630104 +S 0.000000 40 73.421913 71.875229 1.026964 65.979065 64.139290 0.557973 ++ 5.368919 11.837090 1.447202 2.750568 4.407380 0.261077 +S 0.000000 3 21.152515 122.415451 64.395355 55.169041 86.103218 75.309998 ++ 6.047012 5.715814 5.361319 2.815367 6.854795 11.745704 + +32 1 +35 100e +1 104e +3 108e +1 140e +7 81e +36 80d +6 85d +8 815 +26 81d +1 202e +4 80f +2 81f +14 805 +14 82d +2 817 +3 819 +2 85e +11 80e +1 2015 +1 82e +1 a0e +1 84e +1 86e +4 84d +6 809 +1 82f +3 801 +1 859 +1 909 +1 849 +1 200d +1 86d + +a1 a +B 0.000000 682 96.634384 69.807709 4.037755 106.040581 10.876374 35.282578 38.257000 ++ 0.908935 4.538177 4.871448 13.771496 15.306628 25.977404 10.356663 +B 0.000000 637 61.625439 55.131729 68.034592 70.947319 58.361610 4.388794 70.089432 ++ 1.420268 6.462424 6.760890 17.235083 25.171837 15.623380 20.289888 +B 0.000000 436 3.139750 88.942039 38.919056 107.331841 6.884592 26.775358 50.565556 ++ 2.080641 2.013467 5.591082 14.184313 4.022698 13.653090 12.949964 +B 0.000000 14 32.485561 82.781487 99.301003 105.170067 8.422134 35.040237 21.406908 ++ 0.278667 1.803126 0.789956 5.937760 1.656040 1.779775 4.490553 +B 0.000000 2 112.364464 84.578537 10.978918 117.823059 4.422937 31.748272 21.637928 ++ 1.134255 3.704298 3.240845 8.698502 9.250771 7.154522 9.021277 +B 0.000000 1 96.220558 63.515862 26.655439 70.232109 48.434074 10.813900 82.842461 ++ 1.134255 3.704298 3.240845 8.698502 9.250771 7.154522 9.021277 +C 0.000000 636 61.883495 33.717514 35.625050 ++ 1.555357 1.803981 5.919176 +C 0.000000 81 62.128689 70.501625 20.883886 ++ 4.101947 2.574032 6.602085 +C 0.000000 73 63.617352 56.353508 50.105377 ++ 1.105459 1.102154 2.401534 +A 0.000000 3 6.503144 99.993767 72.008400 ++ 4.693197 9.661753 2.939353 +A 0.000000 3 1.901995 100.659409 33.218559 ++ 2.206410 6.669840 0.464900 +A 0.000000 2 52.600075 102.414154 29.665913 ++ 3.449804 8.165796 1.702127 +A 0.000000 1 64.281593 99.641365 75.312332 ++ 3.449804 8.165796 1.702127 +L 0.000000 677 33.125664 87.559364 50.161301 94.640877 ++ 3.116289 6.892664 10.114411 13.950801 +L 0.000000 438 70.594978 58.576836 63.633289 79.825462 ++ 6.665764 7.677237 14.207269 14.062928 +L 0.000000 132 3.943527 63.058315 6.868098 77.010689 ++ 4.587914 6.490368 4.692971 12.476264 +L 0.000000 54 62.719185 65.601295 94.778114 73.396164 ++ 4.570516 4.991409 7.937749 19.987974 +L 0.000000 51 91.336922 43.790867 47.662090 81.499130 ++ 2.563714 8.743991 10.159565 21.951063 +L 0.000000 48 8.148092 57.219883 73.961151 73.586342 ++ 2.594041 4.121870 4.273428 24.152792 +L 0.000000 1 22.631805 100.490555 24.894539 128.000000 ++ 3.461766 4.389996 7.030742 12.305524 +L 0.000000 1 96.399025 58.187229 73.977455 128.000000 ++ 3.461766 4.389996 7.030742 12.305524 +L 0.000000 1 87.076950 94.797356 24.931730 128.000000 ++ 3.461766 4.389996 7.030742 12.305524 +L 0.000000 1 39.263775 73.602051 78.248764 101.713806 ++ 3.461766 4.389996 7.030742 12.305524 +S 1.000000 714 32.331802 68.046097 75.770744 65.644196 63.395660 71.737511 ++ 18.003790 10.599655 10.939954 3.388642 5.806613 9.739329 + +64 1 +141 806047 +85 802047 +33 80e047 +6 80a047 +4 81e047 +15 8020c7 +3 80a0c6 +2 80e046 +9 80a046 +1 808046 +4 80c047 +104 806043 +5 846047 +6 84e043 +4 856043 +16 816047 +41 802043 +41 8020c3 +2 87e600 +4 8000c7 +4 8020c2 +2 80a0c2 +14 846043 +1 80e0c3 +3 80a0c3 +7 800047 +14 804047 +1 840047 +1 844047 +2 842047 +2 814047 +1 84c047 +1 8220c3 +6 80a043 +3 8060c3 +1 84e047 +1 82a0c3 +1 84a043 +1 c7e600 +3 8060c7 +1 967800 +16 816043 +17 826105 +5 80210d +11 806105 +7 82210d +1 aa6905 +6 802105 +1 802115 +1 80211d +6 822105 +4 85e043 +12 80e043 +2 84e042 +3 80e042 +3 81e043 +1 856047 +1 85e042 +4 80a105 +9 82a105 +4 82e105 +1 82a10d +2 808047 +1 802022 + +b1 b +B 1.000000 245 17.990366 65.026787 107.830811 96.424393 22.399569 36.362938 69.632492 ++ 2.268455 3.803004 5.054114 7.945327 10.496049 14.967260 12.748492 +B 0.000000 139 65.202377 38.089653 82.431709 105.040184 4.600776 31.124300 76.123779 ++ 1.426724 3.624135 11.399935 11.441361 1.887777 1.644156 17.913580 +B 0.000000 116 95.456512 53.554913 2.827693 107.237457 9.278842 32.857723 31.128387 ++ 1.252589 2.980449 1.357435 6.326659 4.915345 3.693765 5.647889 +C 1.000000 245 67.289474 48.054840 60.618515 ++ 2.200248 2.299838 7.518516 +L 0.000000 240 95.933121 40.160614 66.159447 108.403633 ++ 2.170816 3.263478 6.299909 13.591332 +L 0.000000 48 126.559380 62.021477 3.631438 77.434807 ++ 2.797931 3.992475 2.636624 11.292583 +L 0.000000 35 62.621723 71.254135 92.801636 70.581116 ++ 4.736046 3.031178 3.560635 9.122393 +S 1.000000 245 20.425343 51.101940 35.998306 51.443581 62.774742 38.887375 ++ 11.568547 14.663874 4.717968 3.541607 6.737890 6.657362 + +19 1 +43 9f +15 bb +3 df +2 8f +12 b9 +41 9d +1 89 +11 bd +11 dd +19 99 +1 ab +3 f9 +2 fd +6 d9 +1 cf +63 9b +2 bf +7 db +2 fb + +c1 cC +B 1.000000 362 0.056252 69.624687 50.294857 62.464184 32.943665 40.885658 123.331596 ++ 1.754054 2.690603 1.250419 18.649288 21.929693 9.130493 12.082167 +B 0.000000 21 31.444508 82.213799 97.536049 107.547653 7.552710 34.033638 25.563108 ++ 1.155956 2.056193 0.780609 4.765981 0.936727 1.529965 4.717772 +C 0.000000 1 86.604416 70.738747 8.378542 ++ 2.000000 2.000000 4.000000 +L 0.000000 122 67.506004 68.127289 95.536285 82.658226 ++ 4.251999 6.419241 4.007284 9.723639 +L 0.000000 70 126.486542 67.105446 3.120451 75.761803 ++ 3.109309 3.179417 3.258505 14.834868 +L 0.000000 68 35.869152 57.138836 47.830410 72.418396 ++ 4.184420 2.977057 5.637128 6.269821 +L 0.000000 49 68.816429 75.238953 24.728590 74.945473 ++ 3.810659 1.526525 7.078124 6.022487 +L 0.000000 43 95.032288 41.951931 49.566765 70.641708 ++ 2.435784 1.284155 9.221992 5.138851 +L 0.000000 20 120.797226 74.787018 70.004395 76.362976 ++ 2.121871 1.229743 4.699326 6.907854 +L 0.000000 20 7.176084 64.469475 81.455399 69.558044 ++ 2.551194 1.900126 1.434512 3.959067 +L 0.000000 1 116.177277 53.613369 9.635715 87.038414 ++ 2.686253 2.215435 3.996810 6.623043 +S 1.000000 362 61.763359 28.746494 76.835373 64.087280 60.768566 76.100777 ++ 6.474957 17.087111 13.454288 3.259578 5.293751 15.040208 + +51 1 +17 803 +99 801 +56 809 +1 823 +22 841 +3 843 +26 811 +2 891 +11 821 +5 831 +4 941 +8 901 +5 849 +3 921 +13 881 +2 861 +2 9c1 +1 961 +16 829 +8 839 +12 819 +4 889 +3 8b1 +1 909 +1 851 +1 8c1 +1 8d1 +2 8b9 +1 8a9 +3 8a1 +2 a21 +1 a41 +3 a01 +1 a39 +1 ab9 +2 aa1 +1 a11 +1 a29 +4 a09 +1 8d9 +2 899 +1 879 +1 a79 +1 82d +1 ae1 +1 aa9 +1 919 +1 c81 +1 859 +1 a81 +1 8c9 + +d1 d +B 1.000000 325 48.839874 67.680145 109.132362 91.803047 25.243362 21.948544 70.629227 ++ 2.742206 4.810615 5.309293 14.768568 44.389519 10.769010 22.201870 +B 0.000000 246 96.898544 73.382088 4.303908 102.338081 13.438613 36.066944 35.571068 ++ 1.040984 5.537887 6.441049 12.931173 13.777465 30.654650 10.934532 +B 0.000000 231 1.841085 93.119102 57.240700 107.618599 4.708021 28.256588 72.475433 ++ 1.521243 3.419281 8.849481 16.132212 2.334780 11.973326 14.355907 +B 0.000000 1 32.686344 71.648010 124.778275 107.689972 9.464801 37.460968 16.080332 ++ 1.683631 4.241001 4.227732 9.101697 7.237504 9.620865 11.499818 +C 1.000000 325 62.011673 50.956341 62.363213 ++ 2.021428 3.540927 8.375170 +C 0.000000 7 69.327026 100.110947 9.648204 ++ 1.043653 1.412025 1.622104 +A 0.000000 1 87.497108 111.201485 -36.000423 ++ 8.000000 8.000000 4.000000 +L 0.000000 318 31.582354 90.971802 75.481651 113.212379 ++ 1.818422 2.111042 8.959855 14.337491 +L 0.000000 93 68.668678 61.377773 96.444008 73.085136 ++ 2.885983 2.763572 3.160134 7.548321 +L 0.000000 63 1.196667 70.751648 2.285902 81.785248 ++ 1.906925 5.476467 2.229991 10.970376 +L 0.000000 9 13.504256 61.846142 15.114764 71.749153 ++ 1.220364 1.196850 1.265544 4.429982 +L 0.000000 1 71.004387 75.679031 -25.506081 81.255257 ++ 1.837674 2.658797 3.772208 7.911804 +L 0.000000 1 115.752869 77.388908 -39.090504 128.000000 ++ 1.837674 2.658797 3.772208 7.911804 +L 0.000000 1 40.597477 83.462440 -35.056313 115.349899 ++ 1.837674 2.658797 3.772208 7.911804 +L 0.000000 1 93.875336 39.756371 53.303879 66.785316 ++ 1.837674 2.658797 3.772208 7.911804 +S 0.000000 319 13.607122 78.004532 95.189209 50.574841 65.749229 91.996239 ++ 13.361881 8.530048 12.409608 4.810889 5.682151 8.499449 +S 0.000000 6 21.596004 91.859985 62.105675 47.718124 74.858246 66.543198 ++ 6.078914 9.170251 2.407112 1.940753 4.845237 4.160451 + +28 1 +111 8097 +3 80b7 +3 82b5 +1 809f +18 8295 +2 8215 +1 8037 +66 8197 +26 8291 +10 8193 +7 8391 +2 10091 +30 8093 +2 8293 +2 10093 +1 10291 +1 c093 +10 8091 +1 10193 +1 8393 +9 8095 +2 8017 +3 8297 +1 8191 +5 8497 +4 8597 +1 b9d7 +2 8117 + +e1 e +B 0.000000 597 126.253052 71.377853 33.647259 68.896141 54.755417 26.355021 100.015984 ++ 1.996231 2.738014 3.369362 4.222009 20.709049 10.447855 10.138552 +B 0.000000 81 126.906822 70.344566 31.820656 61.779636 121.967781 16.650723 75.998955 ++ 3.262666 1.990832 1.964021 2.200702 7.767816 18.431414 13.361873 +B 0.000000 15 127.488487 93.906822 37.792957 96.205544 18.912064 31.140026 28.059896 ++ 1.569423 3.078306 1.943300 7.661791 8.227873 12.650833 5.864563 +B 0.000000 5 4.342581 72.163506 43.314663 60.976154 57.591267 36.993107 100.834099 ++ 0.385404 0.778417 0.927952 1.994986 2.557936 1.135794 3.039920 +B 0.000000 1 114.305107 66.393608 32.226128 83.712166 30.937389 20.309488 94.456932 ++ 1.593180 1.858013 1.793777 3.697357 6.719031 9.704028 7.326754 +B 0.000000 1 0.838946 65.972343 31.265524 67.744576 59.960846 24.814714 51.290455 ++ 1.593180 1.858013 1.793777 3.697357 6.719031 9.704028 7.326754 +B 0.000000 1 126.437553 83.718452 64.434418 112.113937 7.270122 31.038542 21.413626 ++ 1.593180 1.858013 1.793777 3.697357 6.719031 9.704028 7.326754 +C 0.000000 708 65.956825 73.225800 36.451252 ++ 2.107546 3.538565 8.219461 +C 0.000000 23 67.454552 34.463474 30.873987 ++ 1.815165 1.158023 3.270165 +A 0.000000 2 6.751656 91.453880 34.510162 ++ 8.000000 8.000000 4.000000 +A 0.000000 2 1.977459 88.020485 70.236542 ++ 8.000000 8.000000 4.000000 +A 0.000000 2 107.559189 125.750916 49.173664 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 7.364477 77.314217 8.239825 ++ 8.000000 8.000000 4.000000 +L 0.000000 670 2.293456 70.599167 47.332439 89.636429 ++ 3.037926 2.440068 8.114447 15.406995 +L 0.000000 129 67.045242 64.338516 95.476585 72.146736 ++ 3.947526 4.524890 5.802786 4.873528 +L 0.000000 122 126.303101 65.858650 4.154473 75.076118 ++ 8.355043 6.776618 12.978202 24.985386 +L 0.000000 121 67.663864 74.984314 22.660913 72.257782 ++ 3.968055 4.452253 8.559233 22.171473 +L 0.000000 80 94.285439 40.220474 52.671150 73.253464 ++ 5.976016 8.084901 9.255851 23.375389 +L 0.000000 2 7.801173 69.053802 28.144707 100.360771 ++ 3.013783 3.117768 4.685917 8.146338 +L 0.000000 2 30.561628 89.890068 46.662121 71.625038 ++ 3.013783 3.117768 4.685917 8.146338 +L 0.000000 1 48.014771 73.328537 74.291664 91.344086 ++ 3.013783 3.117768 4.685917 8.146338 +L 0.000000 1 85.011421 75.892189 6.379504 101.328194 ++ 3.013783 3.117768 4.685917 8.146338 +L 0.000000 1 26.416862 92.529312 10.568156 96.344414 ++ 3.013783 3.117768 4.685917 8.146338 +S 0.000000 711 109.312294 47.757511 89.696724 53.859291 59.376591 77.070747 ++ 25.523083 13.972374 19.032227 6.422568 5.072157 14.288288 +S 0.000000 2 128.000000 57.056114 88.145088 87.387085 71.000427 88.309868 ++ 17.877789 8.085863 13.216823 4.460116 3.522331 9.922421 + +52 1 +26 802082 +2 826081 +7 822082 +34 812082 +2 800082 +1 806082 +5 80a082 +11 800184 +1 810082 +2 800884 +1 808184 +1 800084 +28 812081 +322 802081 +3 832082 +1 8020a0 +1 826082 +65 806081 +6 800180 +1 8a8180 +1 808180 +1 824180 +1 117e600 +1 820180 +1 107e600 +1 880180 +1 836082 +28 822081 +2 820081 +54 80a081 +8 800081 +12 82a081 +2 804081 +24 80e081 +17 816081 +1 832088 +5 832081 +1 832001 +2 812088 +1 82a088 +1 812008 +1 81a001 +5 836081 +1 8160c1 +1 810081 +10 81a081 +4 81e081 +2 83e081 +1 83a081 +1 828081 +1 82e081 +1 e2b090 + +f1 f +B 0.000000 262 125.754532 76.986137 108.050758 78.172554 30.678347 37.566750 56.834320 ++ 2.125733 3.982306 4.614683 13.602168 33.330780 25.840132 18.075987 +B 0.000000 261 124.390633 72.057648 53.306992 98.615929 9.984797 35.974678 72.832001 ++ 2.088817 3.045077 7.581539 13.720425 4.550086 3.721993 20.890560 +B 0.000000 254 64.498924 48.304989 47.325226 99.297340 6.654332 32.290424 69.983727 ++ 2.410710 4.326707 10.613959 15.214721 2.116083 3.233641 19.700087 +B 0.000000 138 51.533516 53.788422 106.524490 108.964561 8.159419 35.992008 30.835697 ++ 2.743288 6.690366 4.230460 5.917896 3.037456 3.885832 8.335803 +B 0.000000 1 96.329987 67.265594 -0.185615 109.310982 4.431582 30.028948 26.907585 ++ 2.342137 4.511114 6.002014 11.233276 8.212500 5.732922 16.750608 +B 0.000000 1 124.381294 69.828545 116.375282 63.298618 29.810312 128.000000 37.247505 ++ 2.342137 4.511114 6.002014 11.233276 8.212500 5.732922 16.750608 +B 0.000000 1 48.924652 73.594551 135.501816 96.743820 9.703168 32.411995 11.004587 ++ 2.342137 4.511114 6.002014 11.233276 8.212500 5.732922 16.750608 +L 0.000000 155 0.551864 61.584049 2.637279 93.494179 ++ 1.781925 2.979859 2.397443 13.396427 +L 0.000000 125 95.890663 53.413250 42.221340 72.920731 ++ 1.860221 4.768955 8.618095 13.080778 +L 0.000000 123 30.893002 68.601051 39.689194 74.016434 ++ 2.313856 3.904980 14.158802 6.367017 +L 0.000000 64 69.594681 69.307335 133.676788 74.994553 ++ 7.553374 5.476969 7.148921 9.504993 +L 0.000000 8 120.418320 74.651657 122.934395 66.633331 ++ 0.971741 1.338221 2.555497 2.437215 +L 0.000000 6 9.957903 74.300858 79.714409 72.172272 ++ 2.045495 1.047922 1.566218 3.572024 +L 0.000000 6 7.016652 74.512451 131.553177 75.571327 ++ 2.329101 0.800369 3.077994 4.006126 +L 0.000000 1 59.102592 76.161987 97.716370 68.502464 ++ 2.693673 2.772064 4.554240 6.513150 +S 0.000000 243 85.916298 28.249756 90.281776 56.462444 63.742599 104.147331 ++ 15.240814 39.897064 10.825356 7.641721 8.096473 16.410191 +S 0.000000 20 110.239021 4.475606 126.597404 64.828163 59.673550 128.000000 ++ 7.393876 4.689113 2.682951 2.008986 4.806369 0.001000 + +49 1 +23 808f +6 828f +3 818f +3 838f +1 918f +1 c18f +14 848f +1 841f +5 870f +4 850f +3 8707 +38 830f +16 8307 +2 8207 +8 810f +17 1020f +2 1070f +1 8007 +4 820f +1 8305 +2 800f +1 8205 +32 8087 +1 8c87 +1 898f +1 888f +8 8187 +1 8887 +1 8c8f +1 8a8f +5 8787 +4 8287 +3 8587 +10 8387 +1 83a6 +1 8383 +2 8583 +2 8783 +2 8d83 +1 8183 +1 8103 +1 1030f +1 814f +17 8487 +3 a087 +1 a487 +4 9087 +1 b487 +1 a187 + +g1 g9 +B 0.000000 249 127.651146 77.733521 30.917770 80.101326 53.441448 14.832881 86.952919 ++ 1.714936 4.986240 4.335292 7.873734 26.791376 13.105533 23.757050 +B 0.000000 236 67.161057 60.246170 -3.695716 66.189415 84.698906 70.044052 74.714729 ++ 3.238379 5.715706 3.492742 4.004981 26.044020 41.403816 13.215112 +B 0.000000 235 63.021259 39.462940 26.580885 107.755836 8.781215 31.084690 61.476051 ++ 1.578309 2.886018 6.530612 7.126719 3.920274 2.722258 12.484897 +B 0.000000 168 32.242970 78.540215 97.531448 107.373169 9.654013 33.207100 31.869112 ++ 0.812459 3.448273 1.182187 6.726159 4.610196 3.412496 6.496044 +B 0.000000 81 125.008453 93.533043 51.757778 107.815987 6.131124 36.165661 61.978573 ++ 1.651969 1.496806 4.205276 5.395257 1.933713 2.496640 12.292663 +B 0.000000 31 1.144635 86.661865 73.995346 104.651131 9.619892 38.194599 19.535984 ++ 1.692407 1.916887 6.068335 7.802878 6.395379 7.353463 6.732841 +B 0.000000 28 64.321167 36.465309 -3.697618 107.588249 7.765126 31.032999 26.560192 ++ 0.833922 2.596134 5.719634 7.364653 3.864319 2.636344 3.857978 +B 0.000000 13 60.157066 46.398369 18.632679 87.993187 19.297365 28.193361 76.752487 ++ 0.464289 2.120883 0.767177 2.532453 1.014783 1.374444 11.233676 +B 0.000000 4 65.836380 41.455097 10.268631 88.570305 24.853157 54.300652 54.372322 ++ 0.649907 0.676853 1.244420 3.710329 3.808734 9.561567 6.212423 +C 0.000000 251 63.333748 51.243820 60.365665 ++ 2.588331 4.618541 9.805267 +C 0.000000 238 63.376991 66.451874 41.537373 ++ 2.884214 1.348670 5.862376 +C 0.000000 164 63.006710 -14.165306 39.135033 ++ 2.983203 6.000309 6.156552 +C 0.000000 89 62.705437 -21.293003 58.922520 ++ 2.242512 1.724084 2.326550 +L 0.000000 236 31.209406 91.234367 36.534760 98.187386 ++ 1.536751 2.099469 7.726271 9.673767 +L 0.000000 165 59.534039 73.504303 16.142729 80.754189 ++ 4.172653 2.391455 4.317934 11.037558 +L 0.000000 113 61.151596 72.943855 96.920296 84.519165 ++ 1.459784 3.855072 2.133018 9.088217 +L 0.000000 89 60.147690 56.974171 -10.693418 76.162331 ++ 5.360846 3.020900 4.369530 5.889975 +L 0.000000 72 1.006815 64.188087 -36.503151 74.059471 ++ 4.602528 4.061914 3.705145 7.830706 +L 0.000000 68 4.273747 65.996307 23.668982 73.631012 ++ 6.398395 5.803428 17.762541 6.512495 +L 0.000000 47 92.609398 40.254833 6.269110 68.165428 ++ 1.240678 2.029654 8.423848 15.040085 +S 1.000000 489 75.506386 57.509968 56.002930 66.886299 61.861324 60.106842 ++ 20.720390 10.951065 8.481521 4.437459 5.026222 6.664767 + +106 1 +69 10221a +4 11221a +3 14221a +2 10021a +1 10222a +1 10a212 +1 102212 +31 10220a +31 11220a +1 13a202 +10 15220a +1 13220a +1 17220a +2 11a20a +11 10a202 +17 11a202 +2 102b08 +13 112202 +3 15a202 +1 10ab00 +2 132202 +12 102202 +1 12a202 +1 110212 +6 14220a +2 12220a +1 10a20a +1 16a20a +1 11020a +5 142202 +2 152202 +1 12ab00 +2 164c05 +3 108c45 +11 124c05 +3 140c25 +1 1e8c05 +3 10cc25 +1 1a4c05 +1 1e0c25 +1 12cc25 +2 1e4c25 +1 1a4c25 +3 128c05 +9 100c05 +1 1ecc25 +3 14cc25 +1 188c25 +1 108c25 +1 1acc25 +13 10cc05 +1 1a0c05 +2 1a0c25 +1 164c25 +11 12cc05 +2 144c25 +2 184c25 +23 104c05 +1 124c25 +1 160c25 +9 100c45 +6 180c05 +3 108c05 +4 104c45 +1 1c0c05 +2 100c65 +2 10cc45 +2 100a45 +2 120c05 +1 184c05 +1 1c8c05 +2 184a05 +1 144a45 +1 104a65 +1 144c45 +1 14cc05 +1 180c01 +1 180a05 +1 124a05 +1 1a4a05 +3 18cc05 +3 16cc05 +1 1a8c05 +1 168c05 +1 1c4c05 +3 1acc05 +7 101405 +8 101481 +4 121481 +1 101205 +1 101281 +7 10d405 +37 105405 +2 14d405 +5 185405 +1 12d405 +1 101445 +5 145405 +3 181405 +1 145445 +1 109405 +1 1c1405 +1 1a5405 +1 169405 +1 105445 +1 1ad405 + +h1 h +B 1.000000 375 17.053408 64.283623 109.362343 93.562294 25.182968 31.920677 76.943253 ++ 2.023435 3.886304 8.564184 10.535290 13.840322 16.621431 16.249052 +B 0.000000 366 96.199249 65.739960 42.354797 65.286087 66.701668 33.921215 121.008102 ++ 0.691249 1.619494 4.236667 3.739405 26.104898 21.174927 17.162443 +B 0.000000 295 64.218933 36.147198 73.378937 98.563057 4.131432 32.464657 92.748489 ++ 1.431882 4.013927 13.746710 9.439634 1.634877 1.621823 31.834223 +B 0.000000 223 3.990041 94.487137 37.292912 110.631348 6.508278 28.353682 49.658237 ++ 1.262020 3.584887 5.578913 17.044756 6.980771 13.178573 6.248053 +B 0.000000 6 96.447334 57.851307 3.078472 114.664932 4.541387 34.516087 30.065905 ++ 0.090734 6.799956 1.629618 6.493588 1.554409 1.462773 5.901596 +B 0.000000 4 64.363289 38.786293 47.166782 106.518753 4.224357 30.481621 36.738998 ++ 0.242561 1.496219 6.749115 2.833707 1.366652 0.643607 5.014353 +C 0.000000 9 65.648323 45.984200 42.172493 ++ 1.105428 1.920768 3.294447 +C 0.000000 1 32.940937 9.030984 7.861342 ++ 1.105428 1.920768 3.294447 +A 0.000000 1 32.008663 128.000000 6.500588 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 32.393585 104.675201 52.728935 ++ 8.000000 8.000000 4.000000 +L 0.000000 363 95.867439 40.037506 70.425781 109.155930 ++ 2.103350 3.741857 8.149475 13.622331 +L 0.000000 197 33.471069 91.569260 46.989758 74.701706 ++ 2.652945 2.825107 5.301019 6.856849 +L 0.000000 143 96.333565 76.531273 40.990700 71.311150 ++ 4.594181 5.053168 5.582229 27.967363 +L 0.000000 66 29.430637 56.550873 37.895267 72.769768 ++ 3.916990 4.707615 7.484795 23.776678 +L 0.000000 42 66.207047 71.557983 91.277672 75.051346 ++ 6.366040 3.361437 5.080712 9.046875 +L 0.000000 10 28.206438 58.745670 109.555634 71.593887 ++ 0.433083 1.452892 0.702909 1.931736 +L 0.000000 8 0.789098 63.525604 3.873367 99.850449 ++ 1.364149 8.248268 1.192866 26.777725 +L 0.000000 3 8.724504 63.998810 71.960899 66.398117 ++ 1.328341 2.684719 2.916402 0.360793 +L 0.000000 1 64.951134 60.942902 5.890226 128.000000 ++ 2.539820 3.535280 3.461549 8.798864 +L 0.000000 1 92.441628 39.577412 36.407135 73.801315 ++ 2.539820 3.535280 3.461549 8.798864 +S 0.000000 363 48.975689 34.152214 28.683727 47.656647 65.577408 26.084085 ++ 11.892361 11.045789 9.307430 3.730862 14.948371 9.417382 +S 0.000000 9 35.307613 35.898983 57.762379 57.564255 70.183121 18.834646 ++ 4.641919 9.424307 4.402100 1.391737 2.255737 3.564046 +S 0.000000 3 43.472107 59.751987 66.514107 53.142769 82.213181 34.224640 ++ 6.191755 2.372938 1.873857 2.057621 5.922183 7.047631 + +48 1 +122 10040f +33 100c0f +38 100c07 +4 10000f +1 18000f +16 105c03 +9 107c03 +3 102c03 +1 104c03 +25 103c03 +18 101c03 +1 125c03 +1 106c03 +2 103403 +1 107403 +1 100c03 +1 121c03 +1 127c03 +6 10240f +3 110c45 +1 100c55 +6 103c07 +1 10045d +4 102c07 +10 101c07 +3 110c55 +1 101827 +1 11044d +1 107c07 +1 40040f +26 10140f +1 40140f +4 10340f +1 407c0f +1 100407 +1 102807 +1 104c07 +1 109c0f +9 20dc0f +1 150d07 +2 10102f +5 101c0f +1 10048f +1 10002f +1 10360f +1 10041f +1 100807 +1 10100f + +i1 i +B 0.000000 414 64.112602 53.477848 49.314213 94.909569 7.293959 31.907436 74.545677 ++ 1.095734 2.007004 6.004598 10.500868 4.386450 7.626174 20.634024 +B 0.000000 370 3.542384 74.967781 41.846706 107.867027 7.001092 27.295647 59.847118 ++ 2.597308 3.559407 3.805360 14.051576 3.539108 12.140543 11.980013 +B 0.000000 2 96.619568 68.249207 1.241157 109.747604 6.209314 35.896507 24.488922 ++ 1.538768 2.239407 4.087482 10.129454 2.997701 5.575384 12.156263 +B 0.000000 1 9.640382 73.969177 80.187759 103.049416 15.112804 26.400288 35.142746 ++ 1.538768 2.239407 4.087482 10.129454 2.997701 5.575384 12.156263 +A 1.000000 495 52.888416 119.223816 130.946381 ++ 31.699789 6.911944 5.131941 +A 0.000000 98 62.666775 66.294594 50.449402 ++ 4.332973 12.061362 3.901552 +L 0.000000 485 32.132259 72.370789 55.936600 108.693596 ++ 2.216798 2.156382 5.247004 13.544805 +L 0.000000 477 0.321145 64.508049 4.280775 114.709183 ++ 5.564048 2.775024 3.276134 18.544479 +L 0.000000 461 95.961464 57.551540 48.764179 95.005836 ++ 2.173393 2.353018 10.904577 13.926760 +L 0.000000 447 93.506012 60.378815 132.461319 94.786995 ++ 5.398254 4.725142 5.973123 13.197114 +L 0.000000 447 125.574928 65.569099 121.343933 97.632500 ++ 7.387447 4.421193 6.066446 11.756733 +L 0.000000 420 68.431442 61.727901 96.127815 88.417374 ++ 5.929232 4.112555 2.916823 15.422335 +L 0.000000 406 30.873444 72.266106 130.383469 96.297989 ++ 6.300227 4.225338 5.761370 12.236473 +L 0.000000 372 60.959328 67.337738 138.330719 99.291458 ++ 6.710296 3.419491 4.648610 13.149928 +L 0.000000 7 9.352525 68.345825 122.623833 86.121460 ++ 2.730322 1.205445 0.827684 5.493570 +L 0.000000 3 14.442334 69.582634 11.179526 89.269119 ++ 0.591235 0.486951 0.220948 3.859879 +S 0.000000 427 55.859959 60.203632 60.967003 59.548214 60.449837 59.946007 ++ 16.356907 22.030855 8.533275 3.755986 11.322739 11.497515 +S 0.000000 45 64.149612 66.571259 99.195229 64.356918 67.438911 107.433632 ++ 6.746810 15.132523 5.331877 3.442086 5.995644 7.216919 +S 0.000000 8 62.841015 128.000000 70.402161 63.055725 121.659950 70.435898 ++ 2.345517 0.001000 5.054791 1.906456 5.737311 2.606949 +S 0.000000 6 54.832588 128.000000 68.473633 62.080105 124.980286 83.999771 ++ 0.557012 0.001000 3.893299 1.370923 4.393544 4.484370 +S 0.000000 4 58.054382 122.806732 47.254959 67.839966 89.506828 45.904663 ++ 4.213006 5.426945 2.369146 3.498631 2.671601 4.568055 +S 0.000000 4 48.516022 128.000000 71.809128 59.486267 128.000000 123.266823 ++ 3.861477 0.001000 4.221131 0.699317 0.001000 3.213312 +S 0.000000 1 78.025406 128.000000 64.543106 70.454353 128.000000 56.057095 ++ 4.531604 6.486919 4.900587 2.445566 4.443686 5.597853 + +100 1 +3 113d3 +4 133d3 +4 135d3 +5 125d3 +11 137d3 +121 13fd3 +1 127d3 +6 117d3 +8 136d3 +1 112d3 +1 13793 +2 105d3 +1 121d3 +2 106d3 +1 103d3 +2 116d3 +5 11ed3 +1 123d3 +6 13bd3 +45 13ff0 +4 13df0 +1 11ff0 +3 137f0 +3 13bf0 +1 12bf0 +1 127f0 +5 83ff0 +5 43ff0 +1 117f0 +2 2037f0 +1 403ff0 +1 41df0 +1 437f0 +1 83df0 +1 42ff0 +1 2023f0 +1 2007f0 +1 11bf0 +1 136f0 +1 12ff0 +1 10bd1 +2 109d3 +2 10fd1 +39 12fd3 +4 11fd1 +53 11fd3 +1 119d3 +1 11793 +1 10ff1 +1 10bf1 +1 119f1 +1 12bd3 +1 101f1 +1 109d1 +9 13fd1 +3 11ff1 +1 10dd1 +4 13ff1 +2 13ad1 +1 11df1 +1 10bd3 +1 103ed3 +1 101b53 +4 23bd3 +6 23f53 +1 103fd3 +17 23fd3 +4 21f53 +1 21b53 +1 103f53 +1 22f53 +1 20fd3 +3 137f1 +3 137d1 +1 133f1 +1 135f1 +1 13bd1 +1 2bf53 +1 23b53 +7 21fd3 +2 2bb53 +4 13dd3 +1 13cd3 +5 11f93 +4 17dd3 +7 12dd3 +5 12ed3 +1 175d3 +1 13fd7 +1 13b93 +1 17cd1 +2 12fd1 +1 12cd3 +1 13ed7 +1 17cd3 +1 12f9b +2 13ed3 +1 11bd3 +1 13d93 +1 10fd3 + +j1 jJ +B 0.000000 199 61.528793 59.085030 23.965744 96.617485 8.885926 37.186367 92.110123 ++ 2.272508 2.233696 8.239694 16.709400 5.072748 10.125751 26.079069 +B 0.000000 3 0.640538 81.377953 46.811466 112.452789 3.183088 31.123877 42.501110 ++ 0.248748 3.191391 9.666026 3.210104 1.456407 1.122294 6.776017 +B 0.000000 1 32.176048 63.468468 95.025543 109.861229 3.999674 30.373646 27.333666 ++ 1.260628 2.712543 8.952860 9.959752 3.264578 4.077033 16.427544 +B 0.000000 1 0.054862 82.300323 10.712523 96.358643 7.124455 32.996605 32.197376 ++ 1.260628 2.712543 8.952860 9.959752 3.264578 4.077033 16.427544 +C 0.000000 1 61.021530 -22.794197 9.276594 ++ 2.000000 2.000000 4.000000 +A 0.000000 202 63.060520 118.421005 126.558182 ++ 32.648720 7.706487 5.379482 +A 0.000000 4 56.346561 59.070011 27.224762 ++ 4.040989 5.196107 2.158886 +L 0.000000 201 31.216629 80.848000 36.370861 105.814186 ++ 1.838750 5.255558 7.731458 14.022666 +L 0.000000 198 95.458107 66.549278 36.325836 98.955788 ++ 2.790352 4.929332 11.488276 12.479023 +L 0.000000 183 31.654255 80.262634 126.070305 102.537224 ++ 7.832517 6.351031 7.657389 15.514224 +L 0.000000 180 93.867332 68.039276 127.951218 106.241318 ++ 5.892817 4.360603 6.263627 13.448363 +L 0.000000 168 0.971327 63.772442 -34.614941 88.351906 ++ 5.619859 5.168362 3.114241 13.617110 +L 0.000000 167 126.517876 73.701096 116.926514 103.569305 ++ 8.212673 4.677376 6.487085 12.652061 +L 0.000000 153 62.357048 75.252823 136.409927 101.489441 ++ 5.697029 4.191172 4.317953 14.998769 +L 0.000000 149 65.882027 66.938721 95.999245 88.041451 ++ 7.886076 3.826744 3.631678 14.929428 +L 0.000000 72 69.441277 59.414227 -17.205986 77.494057 ++ 4.171077 2.049216 4.547675 8.404123 +L 0.000000 41 127.200066 57.949924 84.028397 86.805687 ++ 2.458067 6.205131 13.489450 13.096167 +L 0.000000 4 11.659056 77.511276 118.047722 80.706909 ++ 2.939747 4.076685 4.583562 9.634985 +L 0.000000 1 112.857208 57.087044 83.378387 73.902077 ++ 4.474807 4.089488 5.518339 12.271375 +L 0.000000 1 78.029648 62.177029 -39.611008 78.854683 ++ 4.474807 4.089488 5.518339 12.271375 +S 0.000000 201 57.578953 127.928467 83.479095 61.168438 90.967133 104.374176 ++ 10.396751 2.833249 14.678378 5.757582 13.384543 30.819323 +S 0.000000 1 77.773788 51.674030 80.163300 71.313622 64.980560 83.815643 ++ 10.396751 0.790708 14.678378 4.797985 13.384543 25.682768 +S 0.000000 1 47.598946 111.598167 97.652130 59.681343 73.171959 114.089561 ++ 10.396751 0.790708 14.678378 4.797985 13.384543 25.682768 + +72 1 +4 11fba1 +10 117fa1 +2 11fda1 +1 117da1 +5 11ffa1 +1 11d7a1 +1 1179a1 +1 115ea1 +2 11dfa1 +2 11efa1 +2 114fa1 +1 11cfa1 +1 11cba1 +1 104fa1 +1 114da5 +1 1147a1 +18 10ffa1 +1 10c9a1 +1 117ba1 +3 116fa1 +30 107fa1 +8 105fa1 +6 101fa1 +21 103fa1 +1 100981 +3 106fa1 +1 105ee1 +1 416fa3 +1 112fa1 +1 100fa1 +4 102fa1 +1 100ba1 +1 107be0 +1 107fe0 +1 108fa1 +3 10bfa1 +1 103fe0 +1 103da1 +1 1027a1 +1 200620 +1 103ba1 +1 121fa1 +2 107ba1 +7 10dfa1 +1 109fa1 +5 10b7a1 +1 1073a3 +2 1075a1 +1 1055a1 +1 1079a1 +2 106da1 +1 1041a1 +2 12fba1 +1 107fb1 +1 10efa1 +1 1067a1 +1 104ba1 +1 1059a1 +1 1051a1 +1 14ffa1 +6 10f7a1 +2 10fea1 +2 10f5a1 +1 10e7a1 +1 10d7a1 +4 1077a1 +1 101f2b +1 1057a1 +1 12f3a1 +1 1097a1 +1 1035a1 +1 1867a1 + +k1 k +B 1.000000 258 1.048409 85.227814 52.142273 97.014671 18.244719 37.376514 100.218575 ++ 1.802679 2.682998 1.755893 5.864843 4.340017 6.156920 18.714748 +B 1.000000 258 96.163902 62.062748 18.390760 76.699608 46.715630 16.154760 68.145935 ++ 0.897914 2.506232 4.317965 5.880265 23.460560 14.020399 14.288998 +B 0.000000 256 19.662975 64.514938 95.104530 79.074150 47.145206 11.893777 83.943314 ++ 2.245577 3.206969 6.547974 5.691977 26.344198 20.783344 25.364626 +B 0.000000 176 63.938965 37.311699 70.425026 99.957428 4.375862 32.689316 87.975441 ++ 1.347371 3.843448 10.462182 11.101024 1.282840 1.737017 18.648291 +B 0.000000 2 22.791851 59.240013 88.183075 65.921814 128.000000 0.337203 35.775856 ++ 1.479832 2.787976 5.441159 7.134527 8.654870 8.228895 18.474384 +B 0.000000 1 96.431252 48.157887 2.880630 109.332764 6.173319 36.242603 22.059855 ++ 1.479832 2.787976 5.441159 7.134527 8.654870 8.228895 18.474384 +C 0.000000 2 59.865662 75.706558 8.559233 ++ 2.000000 2.000000 4.000000 +L 0.000000 252 95.701256 40.907726 68.885803 110.081528 ++ 2.004199 2.898674 7.323682 14.852818 +L 0.000000 152 32.161968 56.150429 102.761597 70.749542 ++ 2.050048 1.872233 4.920319 4.291743 +L 0.000000 64 76.365471 71.992874 81.502274 88.025284 ++ 1.731379 2.760786 2.234143 13.644781 +L 0.000000 63 13.404819 81.755379 76.730652 68.847450 ++ 0.982466 1.500863 2.273736 3.819251 +L 0.000000 28 113.897125 71.454521 20.964296 84.060165 ++ 1.309630 1.359681 1.697742 6.128920 +L 0.000000 2 41.755997 83.739120 35.875916 75.560333 ++ 1.571890 2.078447 3.092103 7.909350 +S 1.000000 258 32.285519 18.015162 45.904049 52.694275 50.394524 27.487974 ++ 13.999010 15.921699 8.671932 3.865051 14.375871 8.386647 + +29 1 +41 208f +5 248f +30 258f +46 218f +2 20cf +2 209b +36 2087 +14 2387 +14 2187 +12 2287 +2 2487 +1 2787 +1 3087 +1 2407 +1 21af +2 250f +1 308f +1 200f +1 210f +1 2587 +12 2b8f +9 2f8f +2 298f +6 238f +9 278f +3 2d8f +1 290f +1 288f +1 228f + +l1 l1I +B 0.000000 241 2.343678 75.277504 58.185608 108.529327 5.171760 28.517654 76.188644 ++ 2.331573 3.111168 6.684707 12.256677 2.621181 9.661535 14.348058 +B 0.000000 225 63.992989 55.001587 73.875397 101.606239 3.948730 32.146542 90.191422 ++ 1.604488 1.990303 12.122506 8.496824 0.762042 1.809348 21.993793 +B 0.000000 40 63.908424 48.804455 65.257050 77.200989 10.015723 35.173077 118.044525 ++ 0.465067 1.312293 2.336390 6.435466 0.854960 1.672770 10.137127 +B 0.000000 1 45.573265 57.498325 140.000000 106.403877 7.802473 29.286209 18.459940 ++ 1.303622 1.965079 6.003022 8.382062 1.412728 2.713807 14.271115 +B 0.000000 1 112.825615 66.795601 9.331129 108.310860 7.892768 35.877342 15.829198 ++ 1.303622 1.965079 6.003022 8.382062 1.412728 2.713807 14.271115 +B 0.000000 1 0.420624 82.061752 19.773022 102.580956 10.113650 35.504654 24.127747 ++ 1.303622 1.965079 6.003022 8.382062 1.412728 2.713807 14.271115 +A 0.000000 87 62.224056 55.787571 63.665543 ++ 3.776769 7.291550 3.631655 +A 0.000000 1 32.819397 128.000000 -14.974708 ++ 3.776769 7.291550 3.026379 +A 0.000000 1 32.339844 128.000000 140.000000 ++ 3.776769 7.291550 3.026379 +L 0.000000 344 95.852585 58.073002 69.515266 109.934441 ++ 1.876206 1.845537 10.311707 18.597857 +L 0.000000 339 31.841707 72.502808 75.882401 114.645851 ++ 2.719798 2.186910 12.966450 21.782015 +L 0.000000 326 0.015056 64.952347 3.203863 112.252319 ++ 5.126899 1.746847 3.594377 15.063411 +L 0.000000 282 67.413605 62.400391 134.223404 91.957199 ++ 6.448859 3.144795 7.506403 15.382194 +L 0.000000 3 67.818054 70.569778 21.261703 66.050270 ++ 0.287679 0.896963 1.231201 2.716246 +L 0.000000 1 64.784676 76.341660 140.000000 128.000000 ++ 2.597406 1.905982 5.449776 10.482508 +L 0.000000 1 0.026212 67.007843 -15.035456 128.000000 ++ 2.597406 1.905982 5.449776 10.482508 +L 0.000000 1 64.688034 67.425064 -15.208798 128.000000 ++ 2.597406 1.905982 5.449776 10.482508 +L 0.000000 1 0.310051 75.922249 140.000000 128.000000 ++ 2.597406 1.905982 5.449776 10.482508 +S 0.000000 327 58.167351 64.191322 60.635162 60.670322 63.062920 59.282471 ++ 15.607806 20.412157 8.065735 5.148342 9.996111 11.597775 +S 0.000000 13 65.294579 63.625656 127.262993 65.811852 64.488632 128.000000 ++ 4.531289 6.337476 1.500118 1.525961 1.460507 0.001000 +S 0.000000 5 54.497932 59.802372 32.122040 65.277565 68.011330 17.876196 ++ 6.856279 6.290040 2.799801 2.175106 2.386585 5.471742 +S 0.000000 1 47.408100 53.106152 23.176626 53.335293 60.254086 29.500402 ++ 6.806621 9.879215 3.673788 2.425435 4.059062 5.045851 + +30 1 +34 40e05 +5 41e05 +1 40a05 +75 41e40 +2 41640 +1 40640 +2 41e41 +5 40e02 +1 41a41 +145 41e03 +14 40e03 +1 59e83 +13 41e02 +3 41e42 +2 101603 +10 41603 +2 100603 +2 40603 +1 41613 +1 101e03 +3 40e42 +1 41c02 +1 225f02 +1 41c03 +1 41e22 +3 83e03 +10 81e03 +4 41a03 +1 40a03 +1 40e0b + +m1 m +B 1.000000 329 32.247650 71.701202 95.645149 103.474243 12.210365 34.042175 43.199055 ++ 0.674149 5.550078 4.370537 10.850891 12.417317 21.390862 9.318017 +B 0.000000 305 96.179123 47.886593 43.251850 65.574211 75.450218 37.732536 118.037178 ++ 0.636999 4.451534 3.705390 2.745473 20.993040 19.959040 17.502636 +B 0.000000 302 96.325859 83.271652 41.995426 65.831406 75.352867 34.116798 117.941566 ++ 0.881710 2.425694 4.056652 2.860659 20.479439 22.800783 20.349237 +B 0.000000 246 64.306374 20.177149 50.088493 97.161118 6.306598 31.631927 71.747894 ++ 1.248247 5.425849 7.002369 9.808049 1.746056 7.599430 12.904522 +B 0.000000 212 33.101154 40.889389 96.392120 99.756508 14.978654 37.844566 29.180847 ++ 1.291811 5.019917 4.458634 15.714692 19.059504 40.563374 12.773856 +B 0.000000 181 3.768965 109.850960 37.123066 110.397041 6.588374 28.321896 49.769672 ++ 1.277228 5.769706 5.235720 14.255170 7.752278 12.525997 10.105548 +B 0.000000 24 96.397842 51.101177 3.785170 103.629929 15.047087 33.636929 29.254749 ++ 0.418741 10.194336 1.215107 10.590664 9.577668 3.482578 8.629766 +B 0.000000 22 96.500946 79.424026 3.455977 104.871964 15.306952 33.505386 29.530426 ++ 0.314129 4.990993 1.333624 13.293109 11.679757 6.533370 8.414232 +C 0.000000 27 78.960693 44.959766 38.241379 ++ 2.766120 2.072496 5.978018 +C 0.000000 24 52.520424 45.525970 37.187592 ++ 2.577206 2.426492 5.448834 +C 0.000000 1 17.223320 7.144403 9.440404 ++ 2.671663 2.249494 5.713426 +L 0.000000 323 95.775978 23.922913 49.110207 94.133369 ++ 2.425709 4.819026 6.047279 14.680464 +L 0.000000 319 32.959297 107.504341 49.080360 96.704605 ++ 3.294706 4.962397 6.087134 11.652185 +L 0.000000 293 96.484474 93.408768 40.861782 93.794182 ++ 2.075571 4.194040 3.181357 8.651260 +L 0.000000 286 31.251381 72.892754 40.468987 84.204834 ++ 4.370391 2.555718 6.594764 8.831623 +L 0.000000 283 31.133303 37.419296 41.283424 84.356087 ++ 3.678171 4.684803 5.559560 9.109868 +L 0.000000 272 96.068550 58.158081 43.185200 89.888618 ++ 2.080210 2.374868 3.425530 9.565449 +L 0.000000 28 62.952919 50.007515 96.141045 78.458389 ++ 3.114052 3.047482 1.783421 6.239651 +L 0.000000 6 127.852707 69.826408 4.764279 82.715981 ++ 1.127669 15.531241 0.807510 4.370671 +L 0.000000 1 17.608898 42.817455 60.742302 65.240280 ++ 2.166363 5.168477 3.690641 9.137647 +L 0.000000 1 102.720276 57.101402 59.472225 76.187958 ++ 2.166363 5.168477 3.690641 9.137647 +S 1.000000 329 78.954193 62.461876 58.107754 67.386589 64.561279 57.333450 ++ 13.922406 11.895398 8.570320 4.937733 4.795941 12.545390 + +62 1 +4 21b81f +91 21f83f +18 21f81f +2 23f82f +1 21383f +1 23782f +3 201bf9 +5 221be9 +1 2213e9 +1 241bb9 +1 2199bb +4 201bd9 +1 201b79 +1 221b69 +1 21701f +2 221bc9 +20 21f82f +5 23f807 +30 21f817 +44 21f807 +1 21e817 +8 21f80f +1 217807 +1 21e807 +2 20782f +1 21e82f +5 21e83f +11 20f83f +1 245a3d +1 201bc9 +2 23998b +1 261b89 +1 22f00f +1 241b49 +1 261b49 +5 20f82f +3 23f80f +1 207a4d +1 23d82f +1 23d80f +1 22f80f +1 21998b +1 25990b +1 29683f +1 20f80f +2 21d81f +7 21783f +1 211817 +1 20d80f +2 21f87f +1 21f85f +6 21b83f +2 21983f +2 21b03f +4 20783f +1 21fc1f +5 20f81f +1 21f03f +1 21781f +1 21683f +1 20781f +1 30f83f + +n1 n +B 0.000000 466 96.221016 65.880127 42.628666 65.540955 65.136169 33.499176 122.715332 ++ 1.132004 1.430161 4.663825 2.934986 22.520151 19.640965 17.533510 +B 0.000000 400 32.968647 58.053131 95.972595 100.174919 15.305019 40.088394 32.883911 ++ 1.180815 4.024724 4.998824 12.525853 14.394125 27.436941 14.175666 +B 0.000000 397 64.352592 35.415405 49.319931 94.919861 6.597249 31.089392 73.432671 ++ 1.528855 2.633651 6.620992 10.883787 2.490365 9.204193 14.170980 +B 0.000000 308 3.788176 94.678162 37.477440 110.367874 6.451895 28.009228 50.172577 ++ 1.700863 3.165289 4.700360 12.757430 4.534128 11.556332 10.007993 +B 0.000000 11 96.548447 56.588295 3.797270 110.835938 6.535447 33.390266 28.510080 ++ 0.346741 9.824404 1.315016 8.808938 4.969743 2.459353 5.900515 +C 0.000000 11 66.853699 48.050938 44.054729 ++ 1.034541 1.791348 2.388063 +L 0.000000 469 32.986012 92.346420 49.786724 95.596733 ++ 2.737354 2.743523 8.855719 11.648461 +L 0.000000 460 95.879715 39.456810 48.397202 94.515976 ++ 3.053056 1.622921 7.833940 13.162826 +L 0.000000 431 96.285805 77.676979 40.772461 93.600243 ++ 1.897019 2.830868 3.792268 9.043732 +L 0.000000 409 31.237309 53.438374 41.397446 83.912048 ++ 3.700517 2.871494 5.372264 13.815991 +L 0.000000 79 67.942726 63.339741 93.927467 81.592308 ++ 6.041812 5.791952 6.151927 13.886495 +L 0.000000 9 127.629562 69.964645 4.160713 102.403305 ++ 0.953988 6.555627 1.071502 24.962435 +L 0.000000 5 8.402470 64.557396 74.406082 66.538757 ++ 1.461749 0.488164 1.376527 0.752456 +S 1.000000 478 75.275635 61.261791 57.922523 66.591148 63.889500 60.558853 ++ 16.839825 9.160315 12.576528 4.832184 6.780993 9.935832 + +45 1 +198 23cf +5 238f +4 22c7 +43 23c7 +26 21cf +3 204f +6 20cf +1 208f +4 20c7 +3 24cd +1 24dc +9 22cf +36 23c3 +11 27c3 +8 27c1 +15 23c1 +1 2781 +2 22c3 +1 21c3 +1 21c1 +2 33c3 +1 33c1 +2 37c1 +10 23cd +1 23c5 +1 22cd +14 27c5 +1 26cd +1 2cec +5 2ce4 +1 24f4 +3 2cf4 +1 2476 +6 27cd +1 26c5 +1 218f +1 23cb +1 2745 +18 27cf +11 234f +11 21c7 +1 235f +1 2347 +1 278d +4 23df + +o1 oO +B 0.000000 1 48.721157 48.914364 82.159470 96.802322 9.854740 32.224262 16.247139 ++ 2.000000 4.000000 4.000000 20.000000 20.000000 20.000000 8.000000 +C 1.000000 522 65.176491 50.574867 71.372253 ++ 1.093939 2.221102 7.731008 +L 0.000000 46 30.340809 92.912086 48.916248 70.739357 ++ 1.896051 2.348481 4.659981 4.779337 +L 0.000000 31 95.099022 36.733894 50.512363 70.809631 ++ 2.123242 1.771747 5.481993 4.928589 +L 0.000000 24 127.686623 64.903557 4.603537 69.127708 ++ 4.655590 6.545293 4.979148 9.439497 +L 0.000000 24 65.939606 63.209984 96.154556 70.661377 ++ 2.558996 3.239114 1.686395 3.659979 +S 0.000000 520 64.971611 64.209259 64.967133 64.684814 64.250908 64.425659 ++ 3.732792 4.610924 5.073050 3.690159 3.407230 4.866238 +S 0.000000 1 65.932106 66.599991 97.950462 67.242455 67.293106 97.561317 ++ 3.110660 2.668358 2.935793 2.135508 1.971776 3.379331 +S 0.000000 1 60.559086 60.893379 83.013672 60.968304 64.408752 77.384758 ++ 3.110660 2.668358 2.935793 2.135508 1.971776 3.379331 + +11 1 +415 42 +25 4a +14 62 +31 46 +1 10a +14 52 +5 56 +10 66 +1 82 +1 43 +5 5a + +p1 pP +B 1.000000 281 113.668610 63.365593 -12.013303 92.946342 23.823753 24.401981 76.403076 ++ 3.274523 4.908048 3.730091 10.018318 12.878125 11.023065 15.970778 +B 0.000000 203 32.758511 58.996120 97.064568 103.490051 12.213902 36.100883 30.966005 ++ 1.144293 6.908731 4.594098 13.295761 9.875551 20.086079 10.277295 +B 0.000000 183 64.371414 35.948601 25.192762 97.735374 4.031087 32.523571 93.671112 ++ 0.502725 2.896266 11.386579 9.252836 1.602147 6.475560 23.524464 +B 0.000000 20 59.357109 43.259064 27.346952 96.078522 4.354187 31.859087 104.992325 ++ 0.382259 0.973787 2.605977 9.187222 0.519975 0.915610 5.494196 +B 0.000000 4 64.580566 37.601086 -10.453959 106.147591 5.876606 35.122307 28.307861 ++ 0.232749 0.928754 13.731899 4.232671 3.831953 7.033803 6.977174 +B 0.000000 1 96.505165 56.326801 -39.366089 110.451675 4.662979 35.117104 27.756784 ++ 1.107310 3.079080 5.964315 7.743126 4.216052 5.929400 10.798676 +C 1.000000 281 69.029579 48.853512 62.401875 ++ 2.702189 2.269634 8.060341 +L 0.000000 272 95.648239 39.859795 27.260086 109.315079 ++ 2.617003 4.171162 6.459060 13.505775 +L 0.000000 82 5.563634 69.826233 3.797882 71.503433 ++ 2.686394 3.350593 3.219569 5.115186 +L 0.000000 76 65.744965 60.028080 96.903992 86.008385 ++ 3.884145 6.392358 4.153298 12.657137 +L 0.000000 44 0.406992 53.417896 -37.716820 69.945839 ++ 2.070823 3.325603 3.129141 2.983233 +S 0.000000 189 106.694687 51.226105 92.877289 76.420837 63.465084 90.725258 ++ 9.270274 7.500899 9.934175 5.085934 8.305172 10.658025 +S 0.000000 78 128.000000 50.292076 116.134247 75.744202 66.281853 106.464615 ++ 0.001000 4.205852 4.510570 2.491904 2.936578 2.377683 +S 0.000000 12 95.601852 36.238338 65.422462 75.082169 55.670029 75.383278 ++ 5.161718 4.785439 2.121409 2.489213 3.301271 2.023532 +S 0.000000 2 115.743935 53.124840 62.453449 83.969971 61.361389 63.261742 ++ 4.810997 5.263738 3.876387 3.217245 4.847674 4.427634 + +42 1 +38 8c7 +8 cc7 +3 dc7 +3 9c7 +19 ac5 +1 a53 +26 8c3 +8 9c3 +6 ac3 +16 ac1 +1 853 +1 bc3 +5 8c1 +2 22c1 +1 a55 +1 20c3 +2 40c3 +5 9c1 +4 bc1 +3 8c5 +8 20c7 +1 847 +1 20c5 +1 ae5 +14 ec5 +2 9cb +2 dcb +13 ccb +1 fc9 +1 8cb +1 ec9 +2 bc5 +1 f45 +1 fc5 +27 10c7 +42 11c7 +2 13c7 +2 12c7 +3 1147 +1 13c5 +1 1047 +1 9d7 + +q1 q +B 1.000000 206 79.772049 64.268074 -9.609321 88.739868 29.272705 50.723915 73.556938 ++ 3.163984 4.911148 4.200903 12.214839 19.588566 37.586330 13.088472 +B 0.000000 116 32.082615 77.381355 97.518837 107.497932 9.099118 32.754143 31.927900 ++ 0.894565 4.075375 1.223115 6.262252 3.711506 2.311426 5.916044 +B 0.000000 84 0.095114 92.638412 23.388855 101.871361 4.368847 31.907959 81.772873 ++ 1.587672 4.407501 12.209359 12.290738 1.097739 1.687850 17.761709 +B 0.000000 1 96.360428 78.145699 -39.673397 115.406288 5.043202 32.085342 29.978075 ++ 1.882074 4.464674 5.644409 9.577341 5.381740 10.033630 12.255408 +C 1.000000 206 63.495903 50.684250 60.980583 ++ 2.082130 1.758004 6.644128 +L 0.000000 201 31.698822 91.516457 30.771542 112.350693 ++ 2.132968 3.833863 13.501166 17.016409 +L 0.000000 45 62.411362 71.787483 98.111481 76.388885 ++ 2.201067 4.592012 1.504415 9.375568 +L 0.000000 29 126.973145 60.956131 4.354155 71.134766 ++ 6.543126 3.857384 6.387291 8.268604 +L 0.000000 28 0.075855 78.597626 -37.474285 67.280647 ++ 1.499795 1.534953 2.563594 2.371734 +L 0.000000 1 92.578636 72.060036 -10.963511 70.710220 ++ 2.405090 3.294809 3.297543 7.326585 +S 1.000000 206 103.921883 80.237053 36.217796 75.124847 66.594902 37.212254 ++ 12.582181 12.865452 8.741820 5.215771 8.787661 5.840743 + +22 1 +38 437 +10 537 +3 4b7 +1 5b7 +1 417 +4 473 +34 433 +14 4b3 +1 413 +16 471 +3 415 +29 431 +13 475 +7 4b1 +10 435 +2 4f1 +1 43b +9 533 +4 571 +4 575 +1 4f5 +1 6f1 + +r1 r +B 1.000000 506 122.002579 72.926941 48.090542 89.780914 14.518917 38.591595 92.594551 ++ 2.938426 2.572754 3.483554 10.707658 5.121342 5.102671 14.899382 +B 0.000000 459 32.657837 69.528549 95.772980 101.868546 14.945996 40.399483 33.631317 ++ 1.005877 3.253453 3.633201 13.673152 19.430702 31.699657 6.815200 +B 0.000000 427 64.294777 46.381519 48.817322 91.159485 7.299184 31.879887 78.487122 ++ 1.306108 2.972785 5.359557 11.884686 2.271223 8.831758 14.122711 +B 0.000000 5 96.411652 59.748444 2.561397 110.935913 5.566938 33.462292 28.840586 ++ 0.279770 1.054564 0.598385 1.525962 0.611581 1.894871 4.037446 +B 0.000000 3 0.196486 87.796768 82.867180 101.944260 15.976243 32.874702 15.877481 ++ 0.253132 0.256867 2.018080 3.823622 4.755073 4.470547 2.283705 +B 0.000000 1 44.933479 51.740879 92.396118 113.814980 8.576086 31.650616 20.042040 ++ 0.978898 2.022084 2.172012 7.091278 4.179528 5.523233 7.960932 +C 0.000000 1 47.471291 6.281808 10.992682 ++ 2.000000 2.000000 4.000000 +L 0.000000 475 95.888336 50.729317 47.993542 93.634995 ++ 2.687573 2.959996 8.779004 13.831631 +L 0.000000 399 31.243256 65.463097 40.995464 81.575813 ++ 3.236072 2.172186 4.363714 9.258805 +L 0.000000 378 0.690796 59.829880 3.735456 86.650093 ++ 1.793777 2.693128 2.181944 7.102993 +L 0.000000 105 66.266983 63.627289 96.493263 85.895393 ++ 3.513777 5.935421 2.198691 17.741119 +L 0.000000 38 11.528427 72.003685 75.563522 90.948662 ++ 3.816601 1.590131 3.038433 10.105516 +S 0.000000 503 98.070297 1.910212 98.574722 68.082253 67.564285 117.064514 ++ 12.594517 7.830218 16.720295 5.332330 13.426648 12.452277 +S 0.000000 3 79.435776 35.100452 100.105499 67.009476 67.741318 107.866936 ++ 3.415127 3.287462 6.043008 2.187797 1.351813 6.932395 + +31 1 +78 1287 +9 1687 +230 1387 +2 1607 +3 1207 +10 1d83 +9 1183 +25 1583 +20 1d81 +5 1983 +1 1505 +7 1581 +3 1083 +2 1b87 +11 1785 +1 1107 +23 1307 +1 1987 +10 1787 +2 1587 +9 1087 +27 1187 +3 1585 +3 2785 +2 1685 +1 11a7 +3 118f +1 110f +3 1397 +1 138f +1 12c7 + +s1 sS +B 0.000000 437 63.418537 59.411243 32.020718 67.818420 59.635586 116.452217 86.324501 ++ 1.989567 2.143378 3.629008 4.037015 31.926275 19.027355 14.524844 +B 0.000000 416 2.098673 71.104973 70.898842 69.088875 52.006580 121.284569 75.792465 ++ 1.788093 7.186102 7.405542 9.841461 21.913267 32.791439 18.038359 +B 0.000000 22 96.559166 50.127930 1.658256 105.648811 6.900963 34.702797 22.546291 ++ 0.279173 3.998817 0.852693 4.346086 1.799275 1.723215 2.840059 +B 0.000000 21 1.880381 90.947548 58.610378 100.896797 16.086239 35.604748 35.625885 ++ 0.849221 1.609352 2.905384 6.480690 5.544969 5.787466 5.931922 +B 0.000000 14 32.340218 76.597122 98.354240 106.569466 5.619655 34.281345 25.356102 ++ 1.385907 1.938034 1.115605 1.949998 1.358767 2.136999 3.003940 +C 0.000000 27 66.777702 69.197594 22.481655 ++ 1.299646 1.183175 3.534296 +A 0.000000 1 3.836915 104.785194 71.303711 ++ 8.000000 8.000000 4.000000 +L 0.000000 389 118.006454 60.339039 44.281746 97.570656 ++ 6.188199 2.057002 3.617082 16.533052 +L 0.000000 342 54.060871 71.883324 59.221840 98.161926 ++ 5.152702 2.431321 4.370430 17.272837 +L 0.000000 215 62.858196 69.431694 98.339653 79.268524 ++ 2.881737 2.850909 2.074657 7.668166 +L 0.000000 158 127.920822 61.300671 2.145379 87.349884 ++ 2.145935 2.995036 1.863366 10.204938 +L 0.000000 71 57.558968 58.723652 24.200190 78.702507 ++ 5.312234 3.475300 4.830414 7.646974 +L 0.000000 47 120.769836 72.195206 77.262505 73.622009 ++ 3.735783 2.296990 5.813509 14.389694 +L 0.000000 3 5.930295 66.741867 84.937073 68.188820 ++ 1.312585 0.517553 0.421846 3.326469 +S 0.000000 430 62.671398 64.916328 40.987827 61.719166 63.907291 56.548470 ++ 9.086356 13.431835 8.931231 3.813514 6.367687 11.891753 +S 0.000000 8 61.070724 74.615036 67.457031 65.949509 63.866928 62.131641 ++ 6.544128 10.747093 3.907657 2.861400 2.422800 4.252401 + +75 1 +54 4783 +3 4d93 +3 4b87 +1 5997 +9 4f83 +1 4a87 +1 4197 +1 5513 +2 4593 +1 5197 +2 4997 +1 5917 +1 4c93 +5 40ad +3 44a9 +2 402d +1 482d +1 40a9 +1 84a9 +29 4583 +2 46a9 +1 48ad +1 40b9 +1 4029 +1 48a9 +69 4183 +20 4083 +3 4883 +1 5083 +10 4103 +8 4303 +2 5583 +5 5183 +83 4383 +8 4483 +3 5903 +1 4229 +2 40a3 +2 4023 +1 5c83 +4 5983 +1 4829 +8 4983 +1 4823 +1 5140 +1 4903 +1 4423 +4 5883 +4 5d83 +1 5003 +1 4803 +3 4a83 +7 5383 +7 4283 +9 4683 +1 4b83 +2 4387 +2 5283 +2 4e83 +4 5f83 +1 4787 +1 5e83 +1 5d03 +6 8783 +1 8583 +2 5783 +5 4703 +1 4503 +2 6183 +1 6303 +2 4003 +1 4c83 +1 4b03 +2 4d83 +1 4403 + +t1 t +B 0.000000 779 127.971481 74.788460 50.788601 79.108154 13.116846 32.266438 88.797104 ++ 2.014729 2.450627 2.860739 11.294030 9.758488 6.955990 17.620438 +B 0.000000 704 67.894249 50.896477 57.953945 109.527130 6.844956 29.050863 50.955151 ++ 2.097174 6.630794 4.566650 5.745431 4.958992 3.294984 11.922171 +B 0.000000 702 13.035578 73.834778 105.023308 104.166786 13.665397 29.933807 42.403179 ++ 3.268653 3.152887 6.111755 6.665466 3.223129 5.679813 10.094704 +B 0.000000 386 53.642784 52.023670 105.428108 110.873642 9.273686 34.410366 32.353291 ++ 2.515232 5.448553 5.522395 5.959321 3.885378 5.070544 6.692665 +B 0.000000 8 124.371063 69.210815 50.912434 65.797104 9.320175 3.767332 80.957939 ++ 0.408637 1.036428 1.243992 5.877431 0.868929 2.932947 4.288234 +B 0.000000 1 48.855816 64.360390 126.223228 108.922127 7.734501 28.236593 15.358098 ++ 2.060885 2.993080 2.987623 6.145307 3.275606 3.369755 8.531741 +L 0.000000 495 96.741814 53.677742 45.297375 74.639763 ++ 2.191667 3.304158 6.041317 8.436039 +L 0.000000 294 127.884209 67.071159 5.936949 79.569191 ++ 7.436816 4.190179 2.587068 12.761840 +L 0.000000 194 32.111805 68.936852 50.150490 71.001167 ++ 2.582550 3.955860 7.611604 4.202947 +L 0.000000 57 10.146840 73.780693 79.219521 77.695663 ++ 1.334716 0.670993 1.516759 4.007484 +L 0.000000 28 78.434608 59.620010 115.480362 94.941635 ++ 4.565620 2.286892 6.649556 13.726661 +L 0.000000 8 61.131317 75.799698 98.380814 64.466385 ++ 0.315236 1.000301 1.247136 0.278227 +L 0.000000 7 66.586929 76.918739 26.607550 67.548203 ++ 5.756320 3.922859 4.281005 1.507601 +L 0.000000 3 63.234932 65.280754 120.666641 69.990440 ++ 1.452045 0.400704 1.166538 4.213754 +S 0.000000 694 73.650818 28.929575 56.607044 51.344837 62.251610 47.840992 ++ 7.880009 25.677275 13.854081 6.353786 7.640216 21.978771 +S 0.000000 93 80.725990 1.745211 112.724709 56.113110 53.567924 124.302361 ++ 7.483100 5.835322 8.617117 3.490106 4.751278 7.984472 + +110 1 +3 504f +87 404f +67 400f +20 408f +4 480f +2 484f +2 488f +8 410f +2 500f +18 414f +17 4147 +55 4047 +23 41cf +4 4187 +28 40c7 +38 40cf +23 41c7 +1 41c3 +1 41cd +27 4087 +14 40cb +6 40c3 +1 60cb +1 6047 +1 44cb +11 404b +4 400b +1 600b +4 4083 +10 814f +3 804b +1 8087 +6 804f +2 808b +5 800b +16 80cf +2 8007 +2 814b +3 8147 +7 81cf +1 810f +1 80cb +3 80c3 +3 8103 +2 8107 +1 4041 +2 41cb +6 4043 +1 4183 +1 4003 +14 4145 +10 4107 +2 4547 +6 41c5 +4 40cd +1 414d +9 40c5 +4 45c5 +16 4045 +4 44c5 +1 44cd +1 454d +4 4445 +1 45e7 +1 404d +1 4545 +1 8156 +1 8154 +1 8487 +1 84c5 +1 4487 +1 8083 +1 8507 +2 8196 +1 9154 +2 81c1 +1 4112 +1 80c1 +1 44c1 +67 4007 +1 850f +1 858f +1 8503 +2 8187 +1 45c7 +1 81c7 +1 81d2 +1 8145 +1 8183 +1 810b +1 81c5 +1 81d0 +1 4141 +1 4285 +1 80c7 +1 82cf +12 4247 +6 4287 +1 42c7 +1 42cf +23 4207 +1 4387 +3 418f +1 424d +2 424f +1 52c5 +4 428f +1 420f +1 4245 +1 4307 + +u1 uU +B 1.000000 294 32.344082 65.632393 57.738041 66.019241 68.958389 33.863811 118.843895 ++ 1.047105 1.256166 4.015125 2.517415 23.686312 26.408573 15.641079 +B 0.000000 226 96.991684 74.152313 3.842182 106.302490 11.478492 35.698883 37.010895 ++ 1.080155 5.981758 6.544886 14.284612 20.989578 34.600903 13.201854 +B 0.000000 175 2.902221 94.382896 42.063622 107.393440 6.668392 26.576069 56.807888 ++ 1.468791 3.345255 3.855270 16.056234 4.503395 13.725299 11.455911 +B 0.000000 172 67.966064 36.909855 60.828533 109.499443 6.745671 28.819033 51.593761 ++ 1.783231 1.892945 5.365258 12.913818 2.675867 9.086118 9.889072 +L 0.000000 287 31.874620 91.849976 54.395554 105.917786 ++ 2.026851 2.635672 4.703421 12.109524 +L 0.000000 280 32.500431 54.920906 59.807896 96.624947 ++ 2.473065 2.940320 3.790594 9.194106 +L 0.000000 277 96.773216 39.572529 49.949902 94.824554 ++ 2.285942 2.126208 8.686955 14.856753 +L 0.000000 227 94.945831 76.665245 60.802868 85.979980 ++ 3.579784 3.148518 6.120341 14.141690 +L 0.000000 79 3.160909 66.757523 4.476694 82.439972 ++ 4.681103 7.381720 5.588319 13.029692 +L 0.000000 4 71.335892 65.640381 24.971199 66.709244 ++ 1.613819 1.200168 3.819045 0.905965 +L 0.000000 1 39.041630 56.339901 34.347469 70.041260 ++ 2.608626 3.179706 4.748529 9.564401 +L 0.000000 1 81.956070 65.752876 37.924316 66.774208 ++ 2.608626 3.179706 4.748529 9.564401 +S 1.000000 294 51.145863 67.028145 54.931042 61.224678 65.490959 58.159832 ++ 10.998899 8.259684 7.756257 3.967161 5.876524 9.034195 + +41 1 +72 10ff +5 10bf +40 107f +8 11fd +2 106f +7 103f +1 117d +5 105f +2 107b +48 10f3 +35 11f1 +8 11f3 +1 11d1 +3 1073 +1 10d3 +1 10d1 +1 10bb +9 10f1 +2 13f3 +1 12f1 +1 12f3 +1 10f7 +1 10c7 +1 10fd +6 11f5 +3 10fb +1 10f5 +2 11f9 +1 10af +1 113d +9 11ff +2 11bf +1 1171 +1 187f +2 117f +1 10cf +1 14df +2 10ef +3 10df +1 1177 +1 1077 + +v1 vV +B 1.000000 246 32.351791 67.549446 73.416939 77.736115 53.608814 33.660950 91.264488 ++ 0.829897 2.422852 7.343572 6.293621 16.851578 15.758283 14.525494 +B 0.000000 54 74.117958 43.466389 55.906147 116.746002 4.370049 30.707365 52.353569 ++ 3.691535 1.500730 4.360497 18.515509 12.004759 15.196943 11.508620 +B 0.000000 36 117.778824 89.465195 58.642544 112.584610 3.334337 34.187828 42.365448 ++ 0.494475 2.413677 4.819043 5.555067 0.721050 0.750953 11.085719 +B 0.000000 3 112.150887 61.671951 14.378730 107.655632 5.272738 34.661594 20.788509 ++ 0.098791 0.098255 0.567993 1.150143 0.205327 0.499051 0.808857 +A 0.000000 1 61.002758 123.181183 62.596966 ++ 8.000000 8.000000 4.000000 +L 0.000000 243 104.265236 47.726601 47.914917 122.827011 ++ 2.976522 3.034788 6.291406 9.898674 +L 0.000000 236 23.189365 81.491829 46.092026 124.764854 ++ 3.012936 4.943545 8.808163 21.422655 +L 0.000000 185 38.912258 58.766869 63.887802 84.253281 ++ 2.391207 3.187271 4.815437 7.640772 +L 0.000000 144 87.900345 73.591530 63.601368 82.361549 ++ 1.657567 7.322375 7.439950 11.529509 +L 0.000000 3 17.067984 72.629326 24.787277 77.580582 ++ 0.620785 0.896953 1.284040 4.092171 +L 0.000000 2 64.490273 51.342121 96.469971 67.859573 ++ 1.778718 2.748854 4.684488 7.923464 +L 0.000000 1 102.775436 41.851917 69.563179 67.295189 ++ 1.778718 2.748854 4.684488 7.923464 +S 0.000000 219 101.803955 50.536652 59.078362 79.754326 66.913239 58.480766 ++ 21.961138 15.553299 9.528308 7.285578 8.125271 14.999471 +S 0.000000 27 75.627853 17.609882 59.257557 70.722588 36.470707 93.063278 ++ 15.727227 11.856354 5.648488 5.259903 6.756680 7.909597 + +24 1 +12 1067 +6 11e3 +13 11e7 +5 10e3 +1 1165 +1 10e7 +8 1063 +2 1065 +88 11e1 +27 1061 +30 10e1 +7 1161 +20 21e1 +1 1071 +2 1461 +5 20e1 +1 1a83 +2 12a3 +4 10a3 +1 21a9 +1 20e9 +1 108b +1 1103 +7 11e5 + +w1 wW +B 0.000000 258 96.244667 62.874035 20.321590 81.131210 47.217316 39.469917 78.854225 ++ 0.956490 4.359797 5.382936 5.171886 11.733747 11.375168 19.628902 +B 0.000000 254 32.235878 83.200073 72.981903 76.156609 58.250320 29.683231 75.159027 ++ 0.753358 4.890212 6.991723 4.362833 11.442469 31.746386 23.911297 +B 0.000000 237 32.367302 50.738300 74.199432 76.325455 60.465927 53.281116 75.130440 ++ 0.603541 2.980417 6.515593 6.149244 17.045109 31.735970 25.432661 +B 0.000000 60 70.538437 28.579718 60.156021 116.077950 5.281207 30.244724 50.731026 ++ 2.213224 3.721112 5.328757 20.988091 14.474094 13.883965 9.119967 +B 0.000000 23 120.017982 97.746513 55.673767 115.039108 4.598456 34.005497 50.080906 ++ 2.124759 7.793733 7.548415 6.451763 4.094082 1.402961 2.237765 +B 0.000000 8 32.524090 53.044334 74.212624 62.387787 114.687187 8.992381 54.874939 ++ 1.040787 3.071027 4.358390 1.205125 9.094528 11.711775 6.496430 +B 0.000000 4 32.676048 45.776257 96.031685 116.311470 4.726443 34.602097 32.206829 ++ 0.606911 8.604010 2.195585 7.354876 2.051157 1.712397 4.874518 +B 0.000000 4 32.492718 88.064407 81.768318 66.788269 103.105453 86.845329 45.027599 ++ 0.209620 1.394430 1.505003 1.830020 5.527290 27.781788 0.526149 +B 0.000000 1 93.182465 95.793419 -35.557281 96.593727 12.846161 19.971483 32.719765 ++ 0.983699 4.111456 3.815609 4.003482 6.789504 13.842034 9.578647 +B 0.000000 1 113.661156 98.690155 31.249268 89.796089 11.653859 49.634819 48.604595 ++ 0.983699 4.111456 3.815609 4.003482 6.789504 13.842034 9.578647 +C 0.000000 12 51.946461 69.052498 30.216795 ++ 1.864524 2.011143 6.864695 +A 0.000000 1 32.659500 128.000000 -39.602077 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 64.256409 97.375481 71.767464 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 67.599831 73.957535 89.733208 ++ 8.000000 8.000000 4.000000 +L 0.000000 256 102.364853 31.720367 49.462517 117.929909 ++ 2.795068 3.902761 9.576177 17.109182 +L 0.000000 247 24.219999 96.027992 46.696129 123.087929 ++ 3.294052 3.884154 7.954340 19.808279 +L 0.000000 163 37.358707 41.680637 66.200500 76.515190 ++ 2.427262 3.647253 4.729640 5.385414 +L 0.000000 155 38.087978 76.095757 67.247368 82.717140 ++ 3.554387 4.659727 5.737158 11.805880 +L 0.000000 130 22.467167 55.817032 29.093262 82.357407 ++ 1.534722 2.986627 2.795464 8.040937 +L 0.000000 130 103.386314 71.270180 29.972193 81.320694 ++ 2.326857 3.447881 3.001942 10.927684 +L 0.000000 123 87.082146 53.930706 69.823486 86.938141 ++ 1.738102 2.540950 2.487126 9.007973 +L 0.000000 102 88.631332 88.751472 65.167099 78.350739 ++ 1.919436 8.923090 7.188677 7.842504 +L 0.000000 7 64.592690 45.706860 97.497307 76.077019 ++ 1.188854 2.482728 0.833187 1.490786 +L 0.000000 3 18.126627 88.196938 28.586210 81.444839 ++ 0.129926 0.791232 0.792193 1.913852 +L 0.000000 1 23.284918 40.668812 72.200211 72.517723 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 0.216702 88.224258 -39.172920 128.000000 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 24.313902 68.326645 61.968483 99.237137 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 103.545509 60.477512 64.629776 116.569572 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 55.407459 64.475159 86.435478 72.272972 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 36.131119 84.433517 93.277664 109.413429 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 87.317352 87.580116 -23.228130 96.369888 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 118.632698 100.085938 -35.065048 102.947517 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 96.950386 79.444633 93.485710 107.452049 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 41.511826 108.142067 -27.747995 95.900505 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 0.112735 83.342934 80.703979 128.000000 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 60.535061 98.224976 -16.396955 72.791870 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 64.922112 87.595474 -39.226452 128.000000 ++ 1.811868 3.208333 3.436969 7.627797 +S 1.000000 259 82.655106 57.457485 59.699615 74.458687 58.299721 58.629131 ++ 15.095680 13.395503 5.705947 5.495281 7.045991 9.142624 + +84 2 +1 1c00f 20 +3 20c007 20 +1 c01f 20 +7 1c007 20 +2 20c00f 20 +1 21c01f 20 +1 20c017 20 +2 21c00f 20 +30 c007 20 +1 21c017 20 +1 1c01f 20 +2 c00f 20 +2 21c007 20 +8 c017 20 +1 2c013 20 +1 c403 20 +33 3fc007 20 +4 37c007 20 +3 13c007 20 +13 3bc007 20 +10 1fc007 20 +1 1bc007 20 +1 1dc007 20 +3 cc007 20 +2 8c007 20 +6 4c007 20 +2 10c007 20 +1 33c007 20 +2 39c007 20 +4 23c007 20 +1 15c007 20 +2 3ec007 20 +9 7c007 20 +1 31c007 20 +1 29c007 20 +3 27c007 20 +1 6c007 20 +1 3ac007 20 +3 5c007 20 +3 3c007 20 +2 2c007 20 +2 bc007 20 +1 c207c907 3a +1 8c085 20 +1 4c085 20 +1 4c023 20 +1 cc023 20 +1 c0a1 20 +2 c023 20 +1 c085 20 +1 ec00f 20 +2 1ec00f 20 +1 1b400f 20 +2 1f400f 20 +1 8b400f 20 +1 9f400f 20 +2 fc00f 20 +12 1fc00f 20 +2 e400f 20 +1 1e4007 20 +1 1bc00f 20 +1 13c00f 20 +1 8f400f 20 +1 2c443 20 +1 23c023 20 +1 40c413 20 +2 40c403 20 +1 48c403 20 +2 1c023 20 +2 42c403 20 +1 9c007 20 +1 ac443 20 +1 8c453 20 +1 c44b 20 +1 60c403 20 +1 1c001000 20 +1 22800f 20 +1 132000f 20 +3 1fc017 20 +18 3fc00f 20 +2 3fc017 20 +1 1f421f 20 +1 201fe007 25 +1 3fc01f 20 + +x1 xX +B 1.000000 219 96.355125 63.247963 13.999931 80.767242 38.038086 32.896530 58.311264 ++ 0.648495 3.211487 4.348387 13.232990 39.852615 9.599280 11.786710 +B 1.000000 219 63.281193 43.896576 50.427814 100.435890 14.947788 33.005886 94.733398 ++ 1.725219 3.350940 1.937853 8.327360 3.091172 3.715927 9.013763 +B 1.000000 219 32.426559 67.982010 87.523476 81.285576 37.829376 36.022549 56.939945 ++ 1.066952 3.207928 3.490776 12.194942 25.930359 14.363316 15.430223 +B 1.000000 219 0.120843 87.344063 50.412525 99.837624 15.042207 32.749798 94.666710 ++ 1.953570 2.894601 2.170016 10.387034 3.444952 4.260798 8.196331 +L 0.000000 56 41.634029 83.133560 34.082119 82.225197 ++ 4.955372 3.254902 5.317792 9.340141 +L 0.000000 49 105.295883 47.801384 69.867119 81.000031 ++ 2.394077 2.874877 3.605093 6.277549 +L 0.000000 27 20.191656 83.828644 70.911194 77.420975 ++ 2.720726 2.080513 2.152343 6.373694 +L 0.000000 24 84.513008 46.774498 30.460661 76.886505 ++ 1.261602 1.112045 1.693888 5.484103 +L 0.000000 7 60.992157 57.252319 93.062233 68.772339 ++ 7.487716 7.914698 4.055992 1.183348 +L 0.000000 5 109.439568 73.686844 16.930790 65.588531 ++ 0.150955 0.276800 0.284585 0.284201 +L 0.000000 5 126.167953 79.185478 7.152898 69.921471 ++ 4.034204 0.546174 2.931940 5.486733 +L 0.000000 4 10.659056 54.046906 11.181263 67.811523 ++ 0.107136 1.087787 0.955513 1.471493 +S 1.000000 219 61.633579 65.729988 64.020096 63.968361 64.269897 58.627922 ++ 5.268442 8.911586 9.507389 4.257296 4.869193 12.371187 + +22 1 +9 104f +95 100f +15 108f +6 10cf +11 106f +27 101f +18 102f +1 112f +15 103f +1 114f +1 140f +1 110f +2 109f +1 141f +3 143f +1 119f +2 111f +1 113f +1 181f +3 180f +3 121f +2 120f + +y1 yY +B 1.000000 283 32.415024 68.630936 72.443581 77.288399 54.325455 32.787834 90.958382 ++ 0.970210 3.229774 4.166248 5.201997 18.640234 15.886462 20.905537 +B 0.000000 280 64.785919 44.860470 19.298120 97.303307 11.987974 43.143604 114.656982 ++ 1.512275 2.929702 5.232248 10.275928 3.303472 10.364510 16.806690 +B 0.000000 48 121.470619 81.913940 25.340366 104.757439 5.705889 29.079250 85.793701 ++ 1.963829 1.719275 13.738225 8.044033 1.853584 2.534754 20.601740 +B 0.000000 3 63.444374 57.162544 -8.412968 73.041412 31.706587 3.862012 92.434616 ++ 0.447283 0.748998 1.457158 1.102664 3.513500 4.554095 4.685905 +B 0.000000 1 112.227417 54.108624 -32.631241 106.405182 6.591868 34.772816 17.937624 ++ 1.032959 2.156937 4.925428 4.638646 4.200089 6.581387 12.579272 +L 1.000000 283 23.533085 77.417046 28.603615 116.623718 ++ 2.103441 4.148291 13.485330 19.390909 +L 0.000000 267 103.991615 45.996246 48.314362 97.017845 ++ 2.862732 4.962595 8.325036 14.568615 +L 0.000000 31 38.967869 56.994308 64.859489 68.340630 ++ 3.081553 4.419709 3.739399 3.283418 +L 0.000000 22 86.203888 69.404968 67.038414 70.100433 ++ 0.648402 1.558100 2.379541 2.704495 +L 0.000000 10 127.680367 55.989136 -33.697571 68.999611 ++ 2.095371 5.567131 2.721957 1.953260 +L 0.000000 3 99.258568 58.282597 28.358868 70.176903 ++ 0.174212 0.291156 1.753930 1.802400 +L 0.000000 1 73.895485 50.867226 -19.354822 82.276474 ++ 1.689669 3.238439 4.536214 6.296350 +S 0.000000 239 110.032219 24.139786 87.490227 66.722984 63.837524 97.504013 ++ 16.653784 18.140585 12.533397 2.796821 10.930485 12.480914 +S 0.000000 44 113.066910 47.678642 17.341673 65.565254 23.400368 35.758411 ++ 12.626781 18.619720 10.361648 2.699383 9.735518 11.735987 + +22 1 +24 1067 +7 1267 +5 1027 +8 2067 +1 10e7 +2 1227 +1 2267 +15 10e3 +154 1063 +17 1163 +4 11e3 +27 2063 +5 20e3 +1 10f3 +2 1423 +1 14a3 +2 10a3 +3 1023 +1 1863 +1 21a9 +1 20e9 +1 2069 + +z1 zZ +B 1.000000 200 63.243301 52.121666 59.530178 79.935303 30.346128 4.033818 87.103630 ++ 1.684175 3.355477 3.706079 7.616447 22.933512 5.111348 13.965971 +B 1.000000 200 0.341543 78.784569 41.086369 80.588898 31.978434 3.561236 90.315559 ++ 1.471563 3.366239 3.368631 5.923500 22.010273 4.605107 14.329727 +B 0.000000 8 96.554031 58.772877 1.685135 114.875473 3.754868 31.735287 34.765381 ++ 0.258663 8.609570 1.033601 3.920547 0.349075 0.665051 3.755219 +B 0.000000 4 32.527084 80.562675 98.582321 106.040672 6.775808 33.779240 26.100882 ++ 0.129466 1.910102 0.967637 5.037095 1.647294 1.412353 3.494154 +L 0.000000 195 64.004868 66.364014 98.246185 107.455406 ++ 2.228899 5.428569 2.230376 17.376507 +L 0.000000 192 127.946167 64.561234 1.850475 112.659782 ++ 1.375359 5.056271 1.749154 16.955082 +L 0.000000 186 19.997036 76.062378 54.266243 104.057739 ++ 2.705093 4.797981 6.727909 17.557537 +L 0.000000 179 83.761864 53.983177 45.701317 103.788528 ++ 2.204089 4.617751 8.689490 18.752487 +L 0.000000 51 64.928383 75.046921 18.459869 72.499321 ++ 4.044360 3.246527 2.230645 5.148387 +L 0.000000 25 127.122482 55.222935 83.618912 70.681557 ++ 3.240227 1.225302 3.088502 6.395640 +S 1.000000 200 60.866608 65.158737 82.533691 63.366776 65.221985 84.404449 ++ 7.452246 12.496560 14.630543 3.919774 3.815327 12.435593 + +18 1 +105 4f3 +13 473 +4 433 +2 573 +32 5f3 +6 4b3 +16 7f3 +8 6f3 +4 4d7 +1 6b3 +1 5b3 +1 4d3 +2 457 +1 4ab +1 4af +1 4eb +1 4e3 +1 4cf + +{1 { +B 1.000000 200 127.673088 76.948845 48.764980 111.008728 6.721030 32.083565 126.945259 ++ 1.849379 4.152786 6.940304 12.463645 1.126888 1.301260 4.594668 +B 0.000000 175 70.116867 52.105934 20.795830 108.948959 8.672771 28.453171 59.025860 ++ 2.565552 4.641238 4.973838 7.523918 1.998415 3.891269 9.999227 +B 0.000000 175 56.411503 54.732529 81.056732 110.514763 8.477478 35.699596 58.194031 ++ 2.117340 1.696769 8.152452 5.203948 2.329195 2.728536 14.810811 +L 0.000000 75 31.331718 70.045601 44.346737 71.782600 ++ 4.799385 4.211577 32.404488 14.556917 +L 0.000000 72 121.031616 70.040268 -31.301460 75.616737 ++ 1.774224 2.572830 5.096611 7.007307 +L 0.000000 27 81.480179 64.200630 102.040947 70.400780 ++ 14.421985 7.885526 33.996635 6.601910 +L 0.000000 13 35.396404 68.808647 11.168782 67.993614 ++ 3.644351 4.390502 4.557734 2.891825 +L 0.000000 2 97.722801 55.413952 48.685635 67.977898 ++ 6.159986 4.765109 19.013866 7.157951 +S 0.000000 121 64.581985 82.335014 65.055061 64.160019 68.984550 70.396408 ++ 5.925170 20.430475 5.943394 3.096610 4.028257 8.423204 +S 0.000000 66 66.162590 5.612901 63.175472 64.083733 49.601189 70.642113 ++ 10.202608 9.889603 6.455539 3.926339 8.612967 12.650011 +S 0.000000 5 50.690166 61.493851 84.739586 68.600845 69.623009 93.372719 ++ 7.430200 5.475628 3.753100 2.525562 3.060655 4.585694 +S 0.000000 5 63.044106 0.638300 109.364876 63.623062 64.273293 128.000000 ++ 6.911271 0.532708 2.436517 2.277982 2.305170 0.001000 +S 0.000000 2 85.852783 5.778881 39.525864 58.481728 47.764870 27.494415 ++ 7.566298 9.000285 4.647137 2.827598 4.462058 5.987449 +S 0.000000 1 56.482979 0.422444 85.010376 64.006477 47.990177 128.000000 ++ 7.566298 9.000285 4.647137 2.827598 4.462058 5.987449 + +48 1 +5 209 +5 201 +3 13f +36 107 +5 227 +2 203 +1 137 +5 205 +6 20f +1 1003 +1 125 +1 221 +6 127 +1 233 +1 10b +1 213 +1 20b +25 117 +4 147 +1 1a5 +16 10f +3 157 +16 11f +1 101 +3 12f +1 105 +1 103 +10 21f +4 217 +8 207 +5 807 +1 22f +1 223 +1 215 +1 225 +1 219 +3 24f +1 211 +1 123 +1 2a9 +1 201f +1 100f +2 25f +1 41f +2 40f +1 417 +1 14f +1 407 + +}1 } +B 1.000000 200 63.559364 53.794117 48.868015 110.751999 6.759519 32.347931 126.945396 ++ 1.778506 3.255461 5.819233 9.268789 1.054920 1.626490 7.631603 +B 0.000000 182 120.484009 76.007164 18.704113 110.881531 8.225213 35.461720 58.853573 ++ 2.000758 1.824448 7.670814 4.185468 2.208225 2.114261 12.859357 +B 0.000000 170 6.355283 79.011963 78.948509 108.784096 8.740952 28.433990 58.829994 ++ 2.749017 3.969042 6.378972 6.391382 2.220545 2.346315 9.246925 +L 0.000000 73 56.670677 60.680046 129.517578 75.430145 ++ 2.569440 3.111424 9.114188 10.622119 +L 0.000000 38 98.835907 61.091808 81.739799 70.350197 ++ 3.979411 4.677207 9.769058 20.880836 +L 0.000000 33 90.950996 59.803204 12.548512 67.688896 ++ 2.633395 3.153071 8.752556 3.056342 +L 0.000000 27 6.557302 58.520748 -32.041336 72.515427 ++ 2.804311 3.642895 5.448702 10.524003 +L 0.000000 17 20.212893 74.895477 23.009594 67.553833 ++ 5.610450 4.115465 18.050678 1.637987 +L 0.000000 4 51.824657 80.472092 60.628979 66.885178 ++ 0.701947 2.403031 4.720799 1.262015 +L 0.000000 1 91.844826 62.991520 35.073673 90.959145 ++ 3.049826 3.415991 8.886615 5.118499 +L 0.000000 1 39.092323 71.653404 91.352516 72.096718 ++ 3.049826 3.415991 8.886615 5.118499 +S 0.000000 187 68.131081 71.150078 67.804169 63.005291 65.406769 71.314919 ++ 9.923227 37.519688 9.653027 3.093933 10.646311 16.980450 +S 0.000000 10 68.461884 122.406807 112.460365 65.246590 65.458321 128.000000 ++ 7.882872 9.113213 6.095694 3.330409 4.132617 0.001000 +S 0.000000 3 66.341537 128.000000 72.708809 66.200493 90.772766 104.959435 ++ 2.937354 0.001000 3.219130 2.018851 0.679702 8.066925 + +43 1 +4 80b +48 807 +6 803 +12 847 +32 80f +2 891 +2 84b +2 813 +1 81b +16 81f +2 865 +4 84f +5 817 +1 857 +2 85f +12 827 +6 887 +2 8c7 +1 8af +4 82f +3 1007 +2 1087 +1 a0f +1 937 +4 1027 +1 10a7 +2 90f +1 907 +2 821 +1 8a1 +3 801 +2 885 +2 811 +1 c13 +1 831 +1 829 +1 809 +1 823 +1 83f +2 2017 +1 201f +1 845 +1 867 + +~1 ~ +B 1.000000 200 95.929482 55.263809 49.540985 109.272064 7.592074 32.088535 51.948685 ++ 0.777016 2.449516 6.594998 5.684134 1.923762 0.965635 6.309652 +B 1.000000 200 31.722961 76.623627 69.425728 108.742126 8.065698 32.432266 49.260254 ++ 0.926193 2.723093 6.129315 3.971954 2.165159 1.642222 6.103605 +L 0.000000 75 82.201660 36.365952 63.851883 90.249504 ++ 3.405952 2.280523 6.313567 16.463619 +L 0.000000 58 18.016506 93.627487 56.298435 93.142319 ++ 4.173990 2.409632 5.736339 18.645557 +L 0.000000 38 102.779007 35.000134 51.252567 75.748268 ++ 4.585302 1.898064 6.689207 8.332808 +L 0.000000 25 123.243507 74.015533 52.297825 80.174850 ++ 1.701501 4.237411 6.090243 10.615335 +L 0.000000 23 57.495289 58.143204 67.278862 75.020836 ++ 3.394267 6.097888 5.253304 10.667645 +L 0.000000 19 37.267071 96.620750 65.076370 71.888298 ++ 5.426067 2.709064 5.326566 5.759484 +L 0.000000 1 80.261490 84.394463 64.149063 97.303970 ++ 3.781180 3.272097 5.901537 11.747408 +S 0.000000 196 68.109436 61.493084 40.710907 65.672264 63.239918 20.555933 ++ 17.926043 14.032889 13.982686 6.020768 4.044930 10.738615 +S 0.000000 3 106.949654 64.673782 3.386474 73.726730 67.412086 2.344930 ++ 14.615839 1.022717 1.624485 2.716229 3.530555 2.349211 +S 0.000000 1 77.168457 113.624458 20.116713 64.406433 59.425316 10.741993 ++ 16.270941 5.383889 6.638361 4.368499 3.787742 6.543913 + +27 1 +57 203 +9 22f +27 213 +13 20b +31 207 +8 20f +7 24f +8 283 +3 293 +7 26f +5 21b +2 247 +1 2a3 +6 287 +2 223 +1 23b +1 24b +1 307 +1 44f +1 40f +2 243 +1 27b +2 22b +1 42f +1 84f +1 2c3 +1 233 + diff --git a/tessdata/tessconfigs/batch b/tessdata/tessconfigs/batch new file mode 100644 index 0000000000..176416473b --- /dev/null +++ b/tessdata/tessconfigs/batch @@ -0,0 +1,79 @@ +################################################# +# Adaptive Matcher Using PreAdapted Templates +################################################# + +acts_fx 0x800 +acts_ocr 0x20 + +RatingScale 30.0 +CertaintyScale 20.0 + +#EnableMatcher 0 +#CurrentFx 2 +MinSlope 0.414213562 +MaxSlope 2.414213562 +#ExtremityMode 1 +NormMethod 1 +EnableAdaptiveMatcher 1 + +NormAdjMidpoint 32.0 +NormAdjCurl 2.0 + +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 + +BuiltInTemplatesFile tessdata/inttemp +BuiltInCutoffsFile tessdata/pffmtable + +EnableLearning 0 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 +ReliableConfigThreshold 2 +MinNumPermClasses 3 + +#EnableStopper 1 +GoodAdaptiveMatch 0.125 +GreatAdaptiveMatch 0.0 + +EnableIntFX 1 +EnableNewAdaptRules 1 +################################################################################ +# +# File: marks/configs/knobs +# RCS: $Header: /cvsroot/tesseract-ocr/tesseract/tessdata/tessconfigs/batch,v 1.3 2007/02/02 23:45:33 theraysmith Exp $ +# Description: Control variables for 'marks' code +# Author: Mark Seaman, OCR Technology +# Created: Wed Feb 27 11:27:27 1991 +# Modified: Tue Jul 30 16:25:37 1991 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ + +#hidden_edges 1 + +save_doc_words 1 +doc_dict_enable 1 +ClassPrunerThreshold 229 +ClassPrunerMultiplier 15 +IntThetaFudge 128 +CPCutoffStrength 0.15 +EvidenceTableBits 9 +IntEvidenceTruncBits 14 +SEExponentialMultiplier 0 +SimilarityCenter 0.0075 +################################################# +# Adaptive Matcher Using 2 Passes +################################################# + +EnableLearning 1 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 + +#save_errors 0 + diff --git a/tessdata/tessconfigs/matdemo b/tessdata/tessconfigs/matdemo new file mode 100755 index 0000000000..24ce73da62 --- /dev/null +++ b/tessdata/tessconfigs/matdemo @@ -0,0 +1,82 @@ +################################################# +# Adaptive Matcher Using PreAdapted Templates +################################################# + +acts_fx 0x800 +acts_ocr 0x20 + +RatingScale 30.0 +CertaintyScale 20.0 + +#EnableMatcher 0 +#CurrentFx 2 +EnableAdaptiveMatcher 1 + +NormAdjMidpoint 32.0 +NormAdjCurl 2.0 + +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 + +BuiltInTemplatesFile tessdata/inttemp +BuiltInCutoffsFile tessdata/pffmtable + +EnableLearning 0 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 +ReliableConfigThreshold 2 +MinNumPermClasses 3 + +#EnableStopper 1 +GoodAdaptiveMatch 0.125 +GreatAdaptiveMatch 0.0 + +EnableIntFX 1 +EnableNewAdaptRules 1 +EnableAdaptiveDebugger 1 +MatchDebugFlags 6 +MatcherDebugLevel 1 +################################################################################ +# +# File: marks/configs/knobs +# RCS: $Header: /cvsroot/tesseract-ocr/tesseract/tessdata/tessconfigs/matdemo,v 1.2 2007/02/02 23:45:33 theraysmith Exp $ +# Description: Control variables for 'marks' code +# Author: Mark Seaman, OCR Technology +# Created: Wed Feb 27 11:27:27 1991 +# Modified: Tue Jul 30 16:25:37 1991 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ + +#hidden_edges 1 + +save_doc_words 1 +doc_dict_enable 1 +ClassPrunerThreshold 229 +ClassPrunerMultiplier 15 +IntThetaFudge 128 +CPCutoffStrength 0.15 +EvidenceTableBits 9 +IntEvidenceTruncBits 14 +SEExponentialMultiplier 0 +SimilarityCenter 0.0075 +################################################# +# Adaptive Matcher Using 2 Passes +################################################# + +display_splits 0 +display_all_words 0 +display_all_blobs 0 +display_segmentations 0 +EnableLearning 1 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 + +#save_errors 0 + diff --git a/tessdata/tessconfigs/old_batch b/tessdata/tessconfigs/old_batch new file mode 100755 index 0000000000..89de538488 --- /dev/null +++ b/tessdata/tessconfigs/old_batch @@ -0,0 +1,82 @@ +################################################# +# Adaptive Matcher Using PreAdapted Templates +################################################# + +acts_fx 0x800 +acts_ocr 0x20 + +RatingScale 30.0 +CertaintyScale 20.0 + +#EnableMatcher 0 +CurrentFx 2 +EnableMicroFeatures 1 +EnableCharNormFeatures 1 +MinSlope 0.414213562 +MaxSlope 2.414213562 +ExtremityMode 1 +NormMethod 1 +EnableAdaptiveMatcher 1 + +NormAdjMidpoint 32.0 +NormAdjCurl 2.0 + +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 + +BuiltInTemplatesFile data/inttemp +BuiltInCutoffsFile data/pffmtable + +EnableLearning 0 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 +ReliableConfigThreshold 2 +MinNumPermClasses 3 + +EnableStopper 1 +GoodAdaptiveMatch 0.125 +GreatAdaptiveMatch 0.0 + +EnableIntFX 1 +EnableNewAdaptRules 1 +################################################################################ +# +# File: marks/configs/knobs +# RCS: $Header: /cvsroot/tesseract-ocr/tesseract/tessdata/tessconfigs/old_batch,v 1.1 2006/06/16 22:17:08 lvincent Exp $ +# Description: Control variables for 'marks' code +# Author: Mark Seaman, OCR Technology +# Created: Wed Feb 27 11:27:27 1991 +# Modified: Tue Jul 30 16:25:37 1991 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ + +#hidden_edges 1 + +save_doc_words 1 +doc_dict_enable 1 +ClassPrunerThreshold 229 +ClassPrunerMultiplier 15 +IntegerMatcherMultiplier 7 +IntThetaFudge 128 +CPCutoffStrength 0.15 +EvidenceTableBits 9 +IntEvidenceTruncBits 14 +SEExponentialMultiplier 0 +SimilarityCenter 0.0075 +################################################# +# Adaptive Matcher Using 2 Passes +################################################# + +EnableLearning 1 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 + +save_errors 0 + diff --git a/tessdata/tessconfigs/segdemo b/tessdata/tessconfigs/segdemo new file mode 100755 index 0000000000..75ab1e66fc --- /dev/null +++ b/tessdata/tessconfigs/segdemo @@ -0,0 +1,74 @@ +################################################# +# Adaptive Matcher Using PreAdapted Templates +################################################# + +acts_fx 0x800 +acts_ocr 0x20 + +RatingScale 30.0 +CertaintyScale 20.0 + +#EnableMatcher 0 +#CurrentFx 2 +EnableAdaptiveMatcher 1 + +NormAdjMidpoint 32.0 +NormAdjCurl 2.0 + +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 + +BuiltInTemplatesFile tessdata/inttemp +BuiltInCutoffsFile tessdata/pffmtable + +EnableLearning 0 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 +ReliableConfigThreshold 2 +MinNumPermClasses 3 + +#EnableStopper 1 +GoodAdaptiveMatch 0.125 +GreatAdaptiveMatch 0.0 + +EnableIntFX 1 +EnableNewAdaptRules 1 +################################################################################ +# +# File: marks/configs/knobs +# RCS: $Header: /cvsroot/tesseract-ocr/tesseract/tessdata/tessconfigs/segdemo,v 1.2 2007/02/02 23:45:33 theraysmith Exp $ +# Description: Control variables for 'marks' code +# Author: Mark Seaman, OCR Technology +# Created: Wed Feb 27 11:27:27 1991 +# Modified: Tue Jul 30 16:25:37 1991 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ + +#hidden_edges 1 + +save_doc_words 1 +doc_dict_enable 1 +ClassPrunerThreshold 229 +ClassPrunerMultiplier 15 +IntThetaFudge 128 +CPCutoffStrength 0.15 +EvidenceTableBits 9 +IntEvidenceTruncBits 14 +SEExponentialMultiplier 0 +SimilarityCenter 0.0075 +################################################# +# Adaptive Matcher Using 2 Passes +################################################# + +display_splits 0 +display_all_words 1 +display_all_blobs 1 +display_segmentations 2 +display_ratings 1 diff --git a/tessdata/tessconfigs/var_batch b/tessdata/tessconfigs/var_batch new file mode 100755 index 0000000000..1918b9ac90 --- /dev/null +++ b/tessdata/tessconfigs/var_batch @@ -0,0 +1,82 @@ +################################################# +# Adaptive Matcher Using PreAdapted Templates +################################################# + +#acts_fx 0x800 +#acts_ocr 0x20 + +#RatingScale 30.0 +#CertaintyScale 20.0 + +#EnableMatcher 0 +#CurrentFx 2 +#EnableMicroFeatures 1 +#EnableCharNormFeatures 1 +#MinSlope 0.414213562 +#MaxSlope 2.414213562 +#ExtremityMode 1 +#NormMethod 1 +#EnableAdaptiveMatcher 1 + +#NormAdjMidpoint 32.0 +#NormAdjCurl 2.0 + +#MinNormScaleX 0.0 +#MaxNormScaleX 0.325 +#MinNormScaleY 0.0 +#MaxNormScaleY 0.325 + +#BuiltInTemplatesFile data/inttemp +#BuiltInCutoffsFile data/pffmtable + +#EnableLearning 0 +#SaveAdaptedTemplates 0 +#UsePreAdaptedTemplates 0 +#ReliableConfigThreshold 2 +#MinNumPermClasses 3 + +#EnableStopper 1 +#GoodAdaptiveMatch 0.125 +#GreatAdaptiveMatch 0.0 + +#EnableIntFX 1 +#EnableNewAdaptRules 1 +################################################################################ +# +# File: marks/configs/knobs +# RCS: $Header: /cvsroot/tesseract-ocr/tesseract/tessdata/tessconfigs/var_batch,v 1.1 2006/06/16 22:17:08 lvincent Exp $ +# Description: Control variables for 'marks' code +# Author: Mark Seaman, OCR Technology +# Created: Wed Feb 27 11:27:27 1991 +# Modified: Tue Jul 30 16:25:37 1991 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ + +#hidden_edges 1 + +#save_doc_words 1 +#doc_dict_enable 1 +#ClassPrunerThreshold 229 +#ClassPrunerMultiplier 15 +#IntegerMatcherMultiplier 7 +#IntThetaFudge 128 +#CPCutoffStrength 0.15 +#EvidenceTableBits 9 +#IntEvidenceTruncBits 14 +#SEExponentialMultiplier 0 +#SimilarityCenter 0.0075 +################################################# +# Adaptive Matcher Using 2 Passes +################################################# + +#EnableLearning 1 +#SaveAdaptedTemplates 0 +#UsePreAdaptedTemplates 0 + +#save_errors 0 + diff --git a/tessdata/test_matrix b/tessdata/test_matrix new file mode 100755 index 0000000000..2fdf2ec1d5 --- /dev/null +++ b/tessdata/test_matrix @@ -0,0 +1,96 @@ + FL RLHT VPOS X N DOT + 12 1234 1234 ENAB +! 11 0011 0110 1 0 1001 1 4 28 0 +" 11 1100 0011 0 1 0100 1 1 0 0 +# 11 0011 0110 0 0 0100 5 5 0 40 +$ 11 0011 0110 0 0 0100 4 5 29 44 +% 11 0111 1111 1 0 1011 1 5 0 0 / +& 11 0011 0110 0 0 0100 4 5 0 0 +' 11 1100 0011 0 0 0100 1 2 0 0 +( 11 0011 0110 0 0 0100 1 5 0 0 +) 11 0011 0110 0 0 0100 1 5 0 0 +* 11 0111 0110 0 0 0100 5 5 0 0 ++ 11 0111 0110 0 0 0100 4 5 0 35 +, 11 1100 1100 0 0 0100 1 2 0 0 +- 11 1000 0110 0 0 0100 1 8 0 0 =_ +. 11 1000 1100 0 0 0100 1 2 0 0 : +/ 11 0011 0110 0 0 0100 1 9 0 0 % +0 01 0011 0110 0 0 0100 1 3 0 0 o +1 01 0011 0110 0 0 0100 1 8 28 0 +2 01 0011 0110 0 0 0100 2 3 0 0 +3 11 0011 0110 0 0 0100 3 3 29 0 +4 11 0011 0110 0 0 0100 3 3 0 48 +5 01 0011 0110 0 0 0100 2 3 0 40 +6 11 0011 0110 0 0 0100 2 3 0 46 +7 01 0011 0110 0 0 0100 1 3 0 37 +8 01 0011 0110 0 0 0100 4 3 0 45 +9 01 0011 0110 0 0 0100 2 3 0 43 g +: 11 1000 1111 1 0 1011 1 0 0 0 +; 11 1100 1100 1 0 1010 1 0 0 0 +< 11 0011 0110 0 0 0100 1 5 0 36 += 11 1000 1111 1 0 1011 1 3 0 0 -_ +> 11 0011 0110 0 0 0100 1 5 0 36 +? 11 0011 0110 1 0 1001 1 5 0 43 +@ 11 0011 0110 0 0 0100 2 5 0 0 +A 01 0011 0110 0 0 0100 2 8 28 45 +B 01 0011 0110 0 0 0100 3 8 28 0 +C 01 0011 0110 0 0 0100 1 8 28 0 c +D 01 0011 0110 0 0 0100 2 8 29 39 +E 01 0011 0110 0 0 0100 2 8 28 38 +F 01 0011 0110 0 0 0100 2 8 28 40 +G 01 0011 0110 0 0 0100 1 8 0 0 +H 01 0011 0110 0 0 0100 2 8 28 36 +I 01 0011 0110 0 0 0100 1 8 28 0 +J 01 0011 0110 0 0 0100 1 8 28 37 j +K 01 0011 0110 0 0 0100 3 8 28 34 +L 01 0011 0110 0 0 0100 1 8 29 0 +M 01 0011 0110 0 0 0100 2 8 28 0 +N 01 0011 0110 0 0 0100 2 8 28 0 +O 01 0011 0110 0 0 0100 1 8 0 0 o +P 01 0011 0110 0 0 0100 2 8 28 0 p +Q 01 0011 0110 0 0 0100 2 8 0 38 +R 01 0011 0110 0 0 0100 3 8 28 40 +S 01 0011 0110 0 0 0100 2 8 29 0 s +T 01 0011 0110 0 0 0100 2 8 29 35 +U 01 0011 0110 0 0 0100 1 8 28 42 u +V 01 0011 0110 0 0 0100 1 8 29 0 v +W 01 0011 0110 0 0 0100 2 8 29 0 w +X 01 0011 0110 0 0 0100 4 8 29 0 x +Y 01 0011 0110 0 0 0100 2 8 0 0 y +Z 01 0011 0110 0 0 0100 2 8 0 0 z +[ 11 0011 0110 0 0 0100 1 5 29 0 +\ 11 0011 0110 0 0 0100 1 9 0 0 +] 11 0011 0110 0 0 0100 1 5 28 0 +^ 11 1110 0011 0 0 0100 1 5 0 0 +_ 11 1000 1000 0 0 0100 1 10 0 0 -= +` 11 1100 0011 0 0 0100 1 3 0 0 +a 10 0110 0110 0 0 0100 2 7 31 48 +b 01 0011 0110 0 0 0100 2 7 28 44 +c 10 0110 0110 0 0 0100 1 7 0 0 C +d 01 0011 0110 0 0 0100 2 7 27 44 +e 10 0110 0110 0 0 0100 2 7 0 0 +f 01 0011 0110 0 0 0100 3 7 0 0 +g 10 0011 1110 0 0 0100 2 7 29 42 +h 01 0011 0110 0 0 0100 2 7 29 42 +i 10 0111 0110 1 0 1010 1 7 28 0 +j 10 0111 0110 1 0 1010 1 7 28 0 J +k 11 0011 0110 0 0 0100 3 7 28 40 +l 01 0011 0110 0 0 0100 1 10 28 0 +m 10 0110 0110 0 0 0100 2 7 28 0 +n 10 0110 0110 0 0 0100 1 7 28 43 +o 10 0110 0110 0 0 0100 1 7 0 0 +p 10 0111 1110 0 0 0100 2 7 28 41 P +q 10 0111 1110 0 0 0100 2 7 28 42 +r 10 0110 0110 0 0 0100 1 7 27 0 +s 10 0110 0110 0 0 0100 2 7 29 0 S +t 11 0011 0110 0 0 0100 3 7 0 47 +u 10 0110 0110 0 0 0100 1 7 28 42 U +v 10 0110 0110 0 0 0100 1 7 29 35 V +w 10 0110 0110 0 0 0100 2 7 29 0 W +x 10 0110 0110 0 0 0100 4 7 29 0 X +y 10 0111 1110 0 0 0100 2 7 0 0 Y +z 10 0110 0110 0 0 0100 2 7 0 0 Z +{ 11 0011 0110 0 0 0100 3 5 0 0 +| 11 0011 0110 0 0 0100 1 5 0 0 +} 11 0011 0110 0 0 0100 3 5 0 0 +~ 11 1100 0011 0 0 0100 2 5 0 0 diff --git a/tessdata/user-words b/tessdata/user-words new file mode 100755 index 0000000000..f3e1f37f51 --- /dev/null +++ b/tessdata/user-words @@ -0,0 +1,920 @@ +a +absurdum +ac +acres +actions +adaption +adjustments +aerobes +affairs +agents +Alan +Albert +Alberta +Alfred +Alice +Alicia +alliances +americas +analysts +announcements +anouncements +apples +applications +apricots +architectures +areas +arguments +arrangements +Arthur +artists +arts +aspects +attitudes +attractions +auctions +aug +az +baccalaureat +backlit +bags +Barbara +Barnabas +Barry +beliefs +benchmarks +Betty +bi +bits +blades +bonaventure +brad +broadminded +broadway +broking +brows +Bruce +bs +buddha +buddhism +buddhist +buddhists +buffers +ca +caffein +calculational +calif +California +cam +cams +Canadian +cancelling +capitulated +caps +Carmel +Carolyn +Carroll +cars +cartridges +cassette +casuality +Catherine +centre +centres +chambermaid +chapters +characteristics +characters +Charles +cheesy +cherokee +Chicago +chloride +Christopher +Chrysler +Churchill +Cicero +cinema +cinemas +Claire +Clara +clark +cleaners +clients +cliffs +clubs +co +codirector +coinsurance +Columbus +combinations +combust +combustor +comparisons +components +computerised +computers +con +concepts +conclusions +connections +connectors +consequences +contemporizing +continued +contra +contractors +controls +coprocessor +corequisite +corp +corridors +corrosive +costmetology +counterparts +cpu +crops +cueing +culturess +curtis +customers +cuts +cutout +cyanide +Czechoslovakia +dan +databases +David +Davis +days +dealership +Deborah +debut +decibles +declarations +deductible +defrayed +degrees +deionized +demobilisation +densily +departments +descriptions +desensitization +desktop +developers +developments +devices +dharma +diameters +Dianne +dicators +differences +digitising +directions +directorate +disadvantages +disassembly +disclosures +discos +discs +discusing +disks +districts +doe +dogs +dominican +dominicans +Donald +dos +dots +Douglas +Douglass +downsize +downsized +drugs +dumplings +duns +eastside +ecconomic +ecconomics +ed +Edward +efforts +Egypt +eh +Einstein +Einsteins +Elaine +electrophotography +elements +Elizabeth +Elliot +emory +emulsion +energized +enquiry +ent +enthusiasts +entrylevel +environments +epilepsy +epistemic +er +eric +erosion +errors +estuary +et +events +everyone's +exactions +exegesis +exhilarating +expenditures +explanations +explicably +expo +ext +extensions +eyelevel +facts +fastidious +fathers +favourably +fax +feb +feint +ferneries +files +filters +Finland +fireplaces +flavours +flights +fluency +fluidized +fluorescent +fm +forceps +forces +forefront +foreknowledge +forman +formfeed +formletters +Francisco +Frankfurt +friends +frond +fronds +frontage +frontseat +ft +functions +funds +futures +futuristic +ga +Galileo +Garfield +Gary +gaskets +geiger +geist +gentiles +Georgia +georgian +giants +gigabytes +glitches +Gloria +gm +gods +gordon +governments +gravitated +gremlins +greyhound +Griffith +groups +guages +Gwen +Hague +halftone +halftones +handfull +hans +hardcopy +harkness +Harold +Harris +haven't +Hawaii +hazards +headlights +headquartered +Helen +helicopter +helicopters +Henderson +herbalists +hermeneutical +hills +hindu +historians +ho +hoc +homeowners +honduras +hong +hours +houses +Howard +hr +hrs +humours +hwy +hyper +hypercard +hypertalk +hz +i +I'd +i.e. +i/o +IBM +iceskating +id +Idaho +ideas +identification +identify +ie +ii +iii +imaged +implementations +inc +individuals +inferences +infrastructure +inoculation +inspectors +instructions +inter +interpretative +intro +intrusive +intrusives +irs +isaac +iso +Italy +its +iv +ix +Jackson +jaguar +Jan +Jane +Jeanne +Jed +Jennie +Jennings +jets +jew +jewish +Joe +John +Jonathan +Joseph +Joyce +jr +jurist +ka +Kansas +Karl +kbytes +Kenneth +Kepler +keyboards +kg +kids +Kirk +kits +knockout +Kong +la +labels +lakeshore +lama +lamps +lan +laptop +Larry +laserjet +laserwriter +latin +Lawrence +laws +layouts +lb +lbs +lcd +le +leaching +Lebanon +leo +leon +Lewis +licensee +licensees +limitations +limpid +Lincoln +Linda +lines +listings +literatures +lithographic +lithography +logo +London +Louis +Lynda +ma +mac +mach +machines +macintosh +macintoshes +maddened +magnetically +manmade +manufacturers +Margaret +Maria +marriages +Martha +Martin +Marvin +Mary +mbyte +mbytes +MD +meads +meals +measurements +mechanics +med +megabit +megabyte +megabytes +members +menus +mesa +methods +Mexico +Meyer +mg +MHz +Michael +micro +microbes +microbial +microbiological +microbiology +microorganism +microorganisms +microsoft +midrange +miles +mils +min +ming +mini +minors +mips +mirages +misnamed +missioned +Missouri +ml +mm +mobilisation +modules +monarchic +monastary +monochrome +month's +Moscow +motorways +msg +mt +multi +multimedia +multiuser +Nurray +museums +nasa +Nathan +nations +nd +ne +Nelson +neoprene +networks +newsletter +Newton +nicholas +nitrate +NJ +nonessential +nonimpact +noninfectious +normative +northside +nov +ns +nuns +nutrients +NY +o +odometer +oem +offcampus +offchip +offsets +offshore +ohm +Olsen +omni +onchip +ondemand +ones +opinions +optimised +options +orchards +oregon +organisations +organise +organises +organising +orginal +os +ot +Otto +outlets +outmoded +overdrive +overdrives +Owens +pages +paperless +papers +Paris +parkway +passages +passengers +passengerside +patio +Paul +payload +pb +pc +pcs +pedal +pedals +penicillin +peoples +perambulate +perils +periods +persons +pesticides +petri +pharmacological +phd +phenomenalists +pheobe +Phil +Philip +philosophers +phlegm +photolithography +photometer +photometrically +photosensitive +phototypsetter +Pierre +plainpaper +plans +platonic +platonism +plc +plots +pluralism +pm +pocketsized +Polly +poly +polygons +polypropylene +popup +potency +pre +precautions +precepts +premises +prep +prerecorded +presswork +pretested +primal +primo +problems +procedures +processors +prod +profits +programme +programs +promissory +propane +protestant +proto +protocols +prudential +ps +pubs +racism +racist +rad +radioactive +rads +Ralph +ramirez +rashers +raster +rd +reactions +realists +realtor +realty +reardeck +rearview +recalibration +recipients +recommendations +recut +redskins +reductions +ref +reflections +refund +reimburse +rel +requirements +resettable +residues +resources +restaurants +rev +revue +rh +rhizomes +rhodes +richard +rickey +risc +rn +Robert +Roberts +rom +roots +Rosemary +rotor +rotors +rovers +rpm +rt +rugby +rumania +rumohra +rumours +Ryan +ryhthm +s +sacrament +sales +samaritan +San +Sandra +santa +Sarah +savagery +schemes +scholasticism +schuler +sciencefiction +sciencehistory +scientists +scripts +scsi +sculptors +se +Seattle +sec +sel +selectable +selectivity +selfpaced +semesters +sensors +Sept +sets +shareholders +sheerest +Shelley +Sheridan +ships +Shirley +shortcomings +Sidney +sig +sinch +Sinclair +singlesheet +singleuser +sinkhole +sists +skiers +slots +socalled +solidstate +souls +sources +southeast +soy +spa +spans +spilt +splines +spots +sq +squelched +sr +st +Stacey +stacks +standalone +states +steps +sterility +Stevens +stoics +stoves +streakings +stucco +students +stylus +sulfuric +summa +summarise +summers +sums +surveyors +susceptibility +swab +swabs +Swiss +Switzerland +sys +systems +t +Taylor +teaparty +tech +techniques +tel +temperatures +Terry +tests +th +theatre +theatres +thinning +thirdparty +Thomas +tickets +timeout +Timothy +tm +Tokyo +tollfree +tom +topics +torah +touted +towels +toxin +toxins +trademarks +traditions +trans +transfers +treatments +trees +trenchant +tribes +trinity +turbidity +turnaround +tx +types +typeset +typesetter +typestyles +typology +U.S. +UK +ultrafine +unassisted +undercut +units +unix +upchucking +upriver +ups +urea +USA +userfriendly +users +utilise +utilised +v +va +vacillated +vehicles +vendors +veneer +vernal +vi +victorian +videotex +Vienna +vii +viii +visitors +vitro +viva +vol +vols +volumes +vt +Wallace +Walter +walz +Wang +warmup +wastebasket +Wayne +ways +weatherproof +well +wellknown +welt +Wesley +westminster +weston +Williams +winchester +windows +Winston +wireframe +Wisconsin +wittmann +words +workstation +workstations +worlds +wows +wraps +x +xerographic +xi +xii +xiii +xiv +xix +xv +xvi +xvii +xviii +xx +years +Yugoslavian +zealots +zion diff --git a/tessdata/word-dawg b/tessdata/word-dawg new file mode 100755 index 0000000000..e86fab6945 Binary files /dev/null and b/tessdata/word-dawg differ diff --git a/tesseract.dsp b/tesseract.dsp new file mode 100755 index 0000000000..598cb3267e --- /dev/null +++ b/tesseract.dsp @@ -0,0 +1,2033 @@ +# Microsoft Developer Studio Project File - Name="tesseract" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=tesseract - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "tesseract.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "tesseract.mak" CFG="tesseract - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "tesseract - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "tesseract - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "tesseract - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "bin.rel" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "aspirin" /I "ccutil" /I "ccstruct" /I "classify" /I "cutil" /I "dict" /I "display" /I "image" /I "textord" /I "viewer" /I "wordrec" /I "." /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "__MSW32__" /D "_AFXDLL" /Yu"mfcpch.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "tesseract - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "bin.dbg" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "aspirin" /I "ccutil" /I "ccstruct" /I "classify" /I "cutil" /I "dict" /I "display" /I "image" /I "textord" /I "viewer" /I "wordrec" /I "." /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "__MSW32__" /D "_AFXDLL" /Yu"mfcpch.h" /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "tesseract - Win32 Release" +# Name "tesseract - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "ccmain" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\ccmain\adaptions.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\applybox.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\baseapi.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\ccmain\blobcmp.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\callnet.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\charcut.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\charsample.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\control.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\docqual.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\expandblob.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\fixspace.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\fixxht.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\imgscale.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\matmatch.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\output.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\paircmp.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\reject.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\scaleimg.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tessbox.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tessedit.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tesseractmain.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tessvars.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tfacepp.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tstruct.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccmain\werdit.cpp +# End Source File +# End Group +# Begin Group "ccstruct" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\ccstruct\blobbox.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\blobs.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\blread.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\callcpp.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\coutln.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\genblob.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\labls.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\linlsq.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\lmedsq.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\mod128.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\normalis.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\ocrblock.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\ocrrow.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\pageblk.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\pageres.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\pdblock.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\points.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\polyaprx.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\polyblk.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\polyblob.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\polyvert.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\poutline.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\quadlsq.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\quadratc.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\quspline.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\ratngs.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\rect.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\rejctmap.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\rwpoly.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\statistc.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\stepblob.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\txtregn.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\vecfuncs.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\werd.cpp +# End Source File +# End Group +# Begin Group "ccutil" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\ccutil\basedir.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\bits16.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\clst.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\debugwin.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\elst.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\elst2.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\errcode.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\getopt.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\globaloc.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\hashfn.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\mainblk.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\memblk.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\memry.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\mfcpch.cpp +# ADD CPP /Yc"mfcpch.h" +# End Source File +# Begin Source File + +SOURCE=.\ccutil\ocrshell.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\serialis.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\strngs.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\tprintf.cpp +# End Source File +# Begin Source File + +SOURCE=.\ccutil\varable.cpp +# End Source File +# End Group +# Begin Group "classify" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\classify\adaptive.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\adaptmatch.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\baseline.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\blobclass.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\chartoname.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\cluster.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\clusttool.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\cutoffs.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\extract.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\featdefs.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\flexfx.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\float2int.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\fpoint.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\fxdefs.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\hideedge.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\intfx.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\intmatcher.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\intproto.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\kdtree.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\mf.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\mfdefs.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\mfoutline.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\mfx.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\normfeat.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\normmatch.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\ocrfeatures.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\outfeat.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\picofeat.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\protos.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\sigmenu.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\speckle.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\classify\xform2d.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# End Group +# Begin Group "cutil" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\cutil\bitvec.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\cutil.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\danerror.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\debug.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\efio.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\emalloc.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\freelist.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\globals.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\listio.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\oldheap.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\oldlist.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\structures.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\tessarray.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\tordvars.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\cutil\variables.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# End Group +# Begin Group "dict" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\dict\choices.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\dict\context.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\dict\dawg.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\dict\hyphen.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\dict\permdawg.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\dict\permnum.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\dict\permute.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\dict\states.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\dict\stopper.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\dict\trie.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# End Group +# Begin Group "display" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\display\cmndwin.cpp +# End Source File +# Begin Source File + +SOURCE=.\display\pagewalk.cpp +# End Source File +# Begin Source File + +SOURCE=.\display\pgedit.cpp +# End Source File +# Begin Source File + +SOURCE=.\display\sbdmenu.cpp +# End Source File +# Begin Source File + +SOURCE=.\display\varabled.cpp +# End Source File +# Begin Source File + +SOURCE=.\display\varblmen.cpp +# End Source File +# Begin Source File + +SOURCE=.\display\varblwin.cpp +# End Source File +# End Group +# Begin Group "image" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\image\bitstrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\image\imgbmp.cpp +# End Source File +# Begin Source File + +SOURCE=.\image\imgio.cpp +# End Source File +# Begin Source File + +SOURCE=.\image\imgs.cpp +# End Source File +# Begin Source File + +SOURCE=.\image\imgtiff.cpp +# End Source File +# End Group +# Begin Group "textord" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\textord\blkocc.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\drawedg.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\drawtord.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\edgblob.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\edgloop.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\fpchop.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\gap_map.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\makerow.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\oldbasel.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\pithsync.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\pitsync1.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\scanedg.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\sortflts.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\topitch.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\tordmain.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\tospace.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\tovars.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\underlin.cpp +# End Source File +# Begin Source File + +SOURCE=.\textord\wordseg.cpp +# End Source File +# End Group +# Begin Group "viewer" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\viewer\evntlst.cpp +# End Source File +# Begin Source File + +SOURCE=.\viewer\evnts.cpp +# End Source File +# Begin Source File + +SOURCE=.\viewer\grphics.cpp +# End Source File +# Begin Source File + +SOURCE=.\viewer\grphshm.cpp +# End Source File +# Begin Source File + +SOURCE=.\viewer\showim.cpp +# End Source File +# End Group +# Begin Group "wordrec" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\wordrec\associate.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\badwords.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\bestfirst.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\chop.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\chopper.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\closed.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\djmenus.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\drawfx.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\findseam.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\gradechop.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\heuristic.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\makechop.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\matchtab.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\matrix.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\metrics.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\mfvars.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\msmenus.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\olutil.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\outlines.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\pieces.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\plotedges.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\plotseg.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\render.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\seam.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\split.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\tally.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\tessinit.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\tface.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\wordrec\wordclass.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# End Group +# Begin Group "ccmain header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\ccmain\adaptions.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\applybox.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\blobcmp.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\callnet.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\charcut.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\charsample.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\control.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\docqual.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\expandblob.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\fixspace.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\fixxht.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\imgscale.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\matmatch.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\paircmp.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\reject.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\scaleimg.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tessbox.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tessedit.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tessvars.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tfacep.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tfacepp.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\tstruct.h +# End Source File +# Begin Source File + +SOURCE=.\ccmain\werdit.h +# End Source File +# End Group +# Begin Group "ccstruct header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\ccstruct\blckerr.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\blobbox.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\blobs.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\blread.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\coutln.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\crakedge.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\genblob.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\hpddef.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\hpdsizes.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\ipoints.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\labls.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\linlsq.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\lmedsq.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\mod128.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\normalis.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\ocrblock.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\ocrrow.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\pageblk.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\pageres.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\pdblock.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\pdclass.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\points.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\polyaprx.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\polyblk.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\polyblob.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\polyvert.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\poutline.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\quadlsq.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\quadratc.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\quspline.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\ratngs.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\rect.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\rejctmap.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\rwpoly.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\statistc.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\stepblob.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\tessclas.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\txtregn.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\vecfuncs.h +# End Source File +# Begin Source File + +SOURCE=.\ccstruct\werd.h +# End Source File +# End Group +# Begin Group "ccutil header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\cutil\array.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\basedir.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\bits16.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\clst.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\debugwin.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\elst.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\elst2.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\errcode.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\fileerr.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\getopt.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\globaloc.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\hashfn.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\host.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\hosthplb.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\lsterr.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\mainblk.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\memblk.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\memry.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\memryerr.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\mfcpch.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\ndminx.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\notdll.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\nwmain.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\ocrclass.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\ocrshell.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\platform.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\secname.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\serialis.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\stderr.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\strngs.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\tprintf.h +# End Source File +# Begin Source File + +SOURCE=.\ccutil\varable.h +# End Source File +# End Group +# Begin Group "classify header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\classify\adaptive.h +# End Source File +# Begin Source File + +SOURCE=.\classify\adaptmatch.h +# End Source File +# Begin Source File + +SOURCE=.\classify\baseline.h +# End Source File +# Begin Source File + +SOURCE=.\classify\blobclass.h +# End Source File +# Begin Source File + +SOURCE=.\classify\chartoname.h +# End Source File +# Begin Source File + +SOURCE=.\classify\cluster.h +# End Source File +# Begin Source File + +SOURCE=.\classify\clusttool.h +# End Source File +# Begin Source File + +SOURCE=.\classify\cutoffs.h +# End Source File +# Begin Source File + +SOURCE=.\classify\extern.h +# End Source File +# Begin Source File + +SOURCE=.\classify\extract.h +# End Source File +# Begin Source File + +SOURCE=.\classify\featdefs.h +# End Source File +# Begin Source File + +SOURCE=.\classify\flexfx.h +# End Source File +# Begin Source File + +SOURCE=.\classify\float2int.h +# End Source File +# Begin Source File + +SOURCE=.\classify\fpoint.h +# End Source File +# Begin Source File + +SOURCE=.\classify\fxdefs.h +# End Source File +# Begin Source File + +SOURCE=.\classify\fxid.h +# End Source File +# Begin Source File + +SOURCE=.\classify\hideedge.h +# End Source File +# Begin Source File + +SOURCE=.\classify\intfx.h +# End Source File +# Begin Source File + +SOURCE=.\classify\intmatcher.h +# End Source File +# Begin Source File + +SOURCE=.\classify\intproto.h +# End Source File +# Begin Source File + +SOURCE=.\classify\kdtree.h +# End Source File +# Begin Source File + +SOURCE=.\classify\matchdefs.h +# End Source File +# Begin Source File + +SOURCE=.\classify\mf.h +# End Source File +# Begin Source File + +SOURCE=.\classify\mfdefs.h +# End Source File +# Begin Source File + +SOURCE=.\classify\mfoutline.h +# End Source File +# Begin Source File + +SOURCE=.\classify\mfx.h +# End Source File +# Begin Source File + +SOURCE=.\classify\normfeat.h +# End Source File +# Begin Source File + +SOURCE=.\classify\normmatch.h +# End Source File +# Begin Source File + +SOURCE=.\classify\ocrfeatures.h +# End Source File +# Begin Source File + +SOURCE=.\classify\outfeat.h +# End Source File +# Begin Source File + +SOURCE=.\classify\picofeat.h +# End Source File +# Begin Source File + +SOURCE=.\classify\protos.h +# End Source File +# Begin Source File + +SOURCE=.\classify\sigmenu.h +# End Source File +# Begin Source File + +SOURCE=.\classify\speckle.h +# End Source File +# Begin Source File + +SOURCE=.\classify\xform2d.h +# End Source File +# End Group +# Begin Group "cutil header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\cutil\bitvec.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\callcpp.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\const.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\danerror.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\debug.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\efio.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\emalloc.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\freelist.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\funcdefs.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\general.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\globals.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\listio.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\oldheap.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\oldlist.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\structures.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\tessarray.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\tordvars.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\util.h +# End Source File +# Begin Source File + +SOURCE=.\cutil\variables.h +# End Source File +# End Group +# Begin Group "dict header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\dict\choicearr.h +# End Source File +# Begin Source File + +SOURCE=.\dict\choices.h +# End Source File +# Begin Source File + +SOURCE=.\dict\context.h +# End Source File +# Begin Source File + +SOURCE=.\dict\dawg.h +# End Source File +# Begin Source File + +SOURCE=.\dict\hyphen.h +# End Source File +# Begin Source File + +SOURCE=.\dict\permdawg.h +# End Source File +# Begin Source File + +SOURCE=.\dict\permnum.h +# End Source File +# Begin Source File + +SOURCE=.\dict\permute.h +# End Source File +# Begin Source File + +SOURCE=.\dict\states.h +# End Source File +# Begin Source File + +SOURCE=.\dict\stopper.h +# End Source File +# Begin Source File + +SOURCE=.\dict\trie.h +# End Source File +# End Group +# Begin Group "display header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\display\cmndwin.h +# End Source File +# Begin Source File + +SOURCE=.\display\pagewalk.h +# End Source File +# Begin Source File + +SOURCE=.\display\pgedit.h +# End Source File +# Begin Source File + +SOURCE=.\display\pgeditx.h +# End Source File +# Begin Source File + +SOURCE=.\display\sbdmenu.h +# End Source File +# Begin Source File + +SOURCE=.\display\varabled.h +# End Source File +# Begin Source File + +SOURCE=.\display\varblmen.h +# End Source File +# Begin Source File + +SOURCE=.\display\varblwin.h +# End Source File +# End Group +# Begin Group "image header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\image\bitstrm.h +# End Source File +# Begin Source File + +SOURCE=.\image\img.h +# End Source File +# Begin Source File + +SOURCE=.\image\imgbmp.h +# End Source File +# Begin Source File + +SOURCE=.\image\imgerrs.h +# End Source File +# Begin Source File + +SOURCE=.\image\imgio.h +# End Source File +# Begin Source File + +SOURCE=.\image\imgs.h +# End Source File +# Begin Source File + +SOURCE=.\image\imgtiff.h +# End Source File +# Begin Source File + +SOURCE=.\image\imgunpk.h +# End Source File +# End Group +# Begin Group "textord header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\textord\blkocc.h +# End Source File +# Begin Source File + +SOURCE=.\textord\blobcmpl.h +# End Source File +# Begin Source File + +SOURCE=.\textord\drawedg.h +# End Source File +# Begin Source File + +SOURCE=.\textord\drawtord.h +# End Source File +# Begin Source File + +SOURCE=.\textord\edgblob.h +# End Source File +# Begin Source File + +SOURCE=.\textord\edgloop.h +# End Source File +# Begin Source File + +SOURCE=.\textord\fpchop.h +# End Source File +# Begin Source File + +SOURCE=.\textord\gap_map.h +# End Source File +# Begin Source File + +SOURCE=.\textord\makerow.h +# End Source File +# Begin Source File + +SOURCE=.\textord\oldbasel.h +# End Source File +# Begin Source File + +SOURCE=.\textord\pithsync.h +# End Source File +# Begin Source File + +SOURCE=.\textord\pitsync1.h +# End Source File +# Begin Source File + +SOURCE=.\textord\scanedg.h +# End Source File +# Begin Source File + +SOURCE=.\textord\sortflts.h +# End Source File +# Begin Source File + +SOURCE=.\textord\tessout.h +# End Source File +# Begin Source File + +SOURCE=.\textord\topitch.h +# End Source File +# Begin Source File + +SOURCE=.\textord\tordmain.h +# End Source File +# Begin Source File + +SOURCE=.\textord\tospace.h +# End Source File +# Begin Source File + +SOURCE=.\textord\tovars.h +# End Source File +# Begin Source File + +SOURCE=.\textord\underlin.h +# End Source File +# Begin Source File + +SOURCE=.\textord\wordseg.h +# End Source File +# End Group +# Begin Group "viewer header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\viewer\evntlst.h +# End Source File +# Begin Source File + +SOURCE=.\viewer\evnts.h +# End Source File +# Begin Source File + +SOURCE=.\viewer\grphics.h +# End Source File +# Begin Source File + +SOURCE=.\viewer\grphshm.h +# End Source File +# Begin Source File + +SOURCE=.\viewer\sbgconst.h +# End Source File +# Begin Source File + +SOURCE=.\viewer\sbgdefs.h +# End Source File +# Begin Source File + +SOURCE=.\viewer\sbgtypes.h +# End Source File +# Begin Source File + +SOURCE=.\viewer\showim.h +# End Source File +# End Group +# Begin Group "wordrec header" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\wordrec\associate.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\badwords.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\bestfirst.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\bitvec.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\charsample.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\chop.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\chopper.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\closed.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\control.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\djmenus.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\drawfx.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\findseam.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\gradechop.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\heuristic.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\makechop.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\matchtab.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\matrix.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\measure.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\metrics.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\mfvars.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\msmenus.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\olutil.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\outlines.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\pieces.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\plotedges.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\plotseg.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\render.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\seam.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\split.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\tally.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\tessinit.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\tface.h +# End Source File +# Begin Source File + +SOURCE=.\wordrec\wordclass.h +# End Source File +# End Group +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\ccmain\tesseractmain.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/tesseract.dsw b/tesseract.dsw new file mode 100755 index 0000000000..d9d14eb7f7 --- /dev/null +++ b/tesseract.dsw @@ -0,0 +1,53 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "cnTraining"=.\training\cnTraining.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "mfTraining"=.\training\mfTraining.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "tesseract"=.\tesseract.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/textord/Makefile.am b/textord/Makefile.am new file mode 100644 index 0000000000..428d2a7e9b --- /dev/null +++ b/textord/Makefile.am @@ -0,0 +1,19 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccstruct -I$(top_srcdir)/ccutil \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + blkocc.h blobcmpl.h drawedg.h drawtord.h edgblob.h \ + edgloop.h fpchop.h gap_map.h makerow.h oldbasel.h \ + pithsync.h pitsync1.h scanedg.h sortflts.h tessout.h \ + topitch.h tordmain.h tospace.h tovars.h underlin.h \ + wordseg.h + +noinst_LIBRARIES = libtesseract_textord.a +libtesseract_textord_a_SOURCES = \ + blkocc.cpp drawedg.cpp drawtord.cpp edgblob.cpp \ + edgloop.cpp fpchop.cpp gap_map.cpp makerow.cpp oldbasel.cpp \ + pithsync.cpp pitsync1.cpp scanedg.cpp sortflts.cpp \ + topitch.cpp tordmain.cpp tospace.cpp tovars.cpp underlin.cpp \ + wordseg.cpp diff --git a/textord/Makefile.in b/textord/Makefile.in new file mode 100644 index 0000000000..9a75fbbda5 --- /dev/null +++ b/textord/Makefile.in @@ -0,0 +1,560 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = textord +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_textord_a_AR = $(AR) $(ARFLAGS) +libtesseract_textord_a_LIBADD = +am_libtesseract_textord_a_OBJECTS = blkocc.$(OBJEXT) drawedg.$(OBJEXT) \ + drawtord.$(OBJEXT) edgblob.$(OBJEXT) edgloop.$(OBJEXT) \ + fpchop.$(OBJEXT) gap_map.$(OBJEXT) makerow.$(OBJEXT) \ + oldbasel.$(OBJEXT) pithsync.$(OBJEXT) pitsync1.$(OBJEXT) \ + scanedg.$(OBJEXT) sortflts.$(OBJEXT) topitch.$(OBJEXT) \ + tordmain.$(OBJEXT) tospace.$(OBJEXT) tovars.$(OBJEXT) \ + underlin.$(OBJEXT) wordseg.$(OBJEXT) +libtesseract_textord_a_OBJECTS = $(am_libtesseract_textord_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_textord_a_SOURCES) +DIST_SOURCES = $(libtesseract_textord_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccstruct -I$(top_srcdir)/ccutil \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + blkocc.h blobcmpl.h drawedg.h drawtord.h edgblob.h \ + edgloop.h fpchop.h gap_map.h makerow.h oldbasel.h \ + pithsync.h pitsync1.h scanedg.h sortflts.h tessout.h \ + topitch.h tordmain.h tospace.h tovars.h underlin.h \ + wordseg.h + +noinst_LIBRARIES = libtesseract_textord.a +libtesseract_textord_a_SOURCES = \ + blkocc.cpp drawedg.cpp drawtord.cpp edgblob.cpp \ + edgloop.cpp fpchop.cpp gap_map.cpp makerow.cpp oldbasel.cpp \ + pithsync.cpp pitsync1.cpp scanedg.cpp sortflts.cpp \ + topitch.cpp tordmain.cpp tospace.cpp tovars.cpp underlin.cpp \ + wordseg.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu textord/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu textord/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_textord.a: $(libtesseract_textord_a_OBJECTS) $(libtesseract_textord_a_DEPENDENCIES) + -rm -f libtesseract_textord.a + $(libtesseract_textord_a_AR) libtesseract_textord.a $(libtesseract_textord_a_OBJECTS) $(libtesseract_textord_a_LIBADD) + $(RANLIB) libtesseract_textord.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blkocc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drawedg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drawtord.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edgblob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edgloop.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fpchop.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gap_map.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/makerow.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldbasel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pithsync.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pitsync1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scanedg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sortflts.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/topitch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tordmain.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tospace.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tovars.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/underlin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wordseg.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/textord/blkocc.cpp b/textord/blkocc.cpp new file mode 100644 index 0000000000..2dd10a8c59 --- /dev/null +++ b/textord/blkocc.cpp @@ -0,0 +1,810 @@ +/***************************************************************************** + * + * File: blkocc.cpp (Formerly blockocc.c) + * Description: Block Occupancy routines + * Author: Chris Newton + * Created: Fri Nov 8 + * Modified: + * Language: C++ + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1991, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************/ + +/* +---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------- +*/ + +#include "mfcpch.h" +#include +#include +#include "errcode.h" +#include "grphics.h" +#include "drawtord.h" +#include "blkocc.h" +#include "notdll.h" + +const ERRCODE BLOCKOCC = "BlockOcc"; + +ELISTIZE (REGION_OCC) +#define EXTERN +EXTERN BOOL_VAR (blockocc_show_result, FALSE, +"Show intermediate results"); + +/* The values given here should reflect the values of bln_x_height and + * bln_baseline_offset. These are defined as part of the word class + * definition */ + +EXTERN INT_VAR (blockocc_desc_height, 0, +"Descender height after normalisation"); +EXTERN INT_VAR (blockocc_asc_height, 255, +"Ascender height after normalisation"); + +EXTERN INT_VAR (blockocc_band_count, 4, "Number of bands used"); + +EXTERN double_VAR (textord_underline_threshold, 0.5, +"Fraction of width occupied"); + +/* ******************************************************************** +A note on transitions. + +We want to record occupancy in various bands. In general we need to consider +7 situations: + +(1) (2) (3) (4) +\ / \ / \ / +__\_____/_____\_________/_____\_________/______ Upper Limit + \ / \ / \ / + / \ \-->--/ \--<--/ /-----\ +v ^ / \(7) +\ \ \ / + \ \ /--<--\ /-->--\ \-----/ +____\______\____/_______\____/_______\_________ Lower Limit + \ \ / \ / \ + (5) (6) +We know that following "next" pointers around an outline keeps the black area +on the LEFT. We only need be concerned with situations 1,2,3,5 and 7. +4 and 6 can be ignored as they represent small incursions into a large black +region which will be recorded elsewhere. Situations 3 and 5 define encloseed +areas bounded by the upper and lower limits respectively. Situation 1 is open +to the right, awaiting a closure by a situation 2 which is open to the right. +Situation 7 is entirely enclosed within the band. + +The situations are refered to as region types and are determined by +find_region_type. + +An empty region type is used to denote entry to an adjacent band and return +to the original band at the same x location. +***********************************************************************/ + +#define REGION_TYPE_EMPTY 0 +#define REGION_TYPE_OPEN_RIGHT 1 +#define REGION_TYPE_OPEN_LEFT 2 +#define REGION_TYPE_UPPER_BOUND 3 +#define REGION_TYPE_UPPER_UNBOUND 4 +#define REGION_TYPE_LOWER_BOUND 5 +#define REGION_TYPE_LOWER_UNBOUND 6 +#define REGION_TYPE_ENCLOSED 7 + +BAND bands[MAX_NUM_BANDS + 1]; // band defns + +/********************************************************************** + * test_underline + * + * Check to see if the blob is an underline. + * Return TRUE if it is. + **********************************************************************/ + +BOOL8 test_underline( //look for underlines + BOOL8 testing_on, //drawing blob + PBLOB *blob, //blob to test + float baseline, //coords of baseline + float xheight //height of line + ) { + INT16 occ; + INT16 blob_width; //width of blob + BOX blob_box; //bounding box + float occs[MAX_NUM_BANDS + 1]; //total occupancy + + blob_box = blob->bounding_box (); + set_bands(baseline, xheight); //setup block occ + blob_width = blob->bounding_box ().width (); + if (testing_on) { + // blob->plot(to_win,GOLDENROD,GOLDENROD); + // line_color_index(to_win,GOLDENROD); + // move2d(to_win,blob_box.left(),baseline); + // draw2d(to_win,blob_box.right(),baseline); + // move2d(to_win,blob_box.left(),baseline+xheight); + // draw2d(to_win,blob_box.right(),baseline+xheight); + tprintf + ("Testing underline on blob at (%d,%d)->(%d,%d), base=%g\nOccs:", + blob->bounding_box ().left (), blob->bounding_box ().bottom (), + blob->bounding_box ().right (), blob->bounding_box ().top (), + baseline); + } + block_occ(blob, occs); + if (testing_on) { + for (occ = 0; occ <= MAX_NUM_BANDS; occ++) + tprintf ("%g ", occs[occ]); + tprintf ("\n"); + } + if (occs[1] > occs[2] + occs[2] && occs[1] > occs[3] + occs[3] + && occs[1] > blob_width * textord_underline_threshold) + return TRUE; //real underline + if (occs[4] > occs[2] + occs[2] + && occs[4] > blob_width * textord_underline_threshold) + return TRUE; //overline + return FALSE; //neither +} + + +/********************************************************************** + * test_underline + * + * Check to see if the blob is an underline. + * Return TRUE if it is. + **********************************************************************/ + +BOOL8 test_underline( //look for underlines + BOOL8 testing_on, //drawing blob + C_BLOB *blob, //blob to test + INT16 baseline, //coords of baseline + INT16 xheight //height of line + ) { + INT16 occ; + INT16 blob_width; //width of blob + BOX blob_box; //bounding box + INT32 desc_occ; + INT32 x_occ; + INT32 asc_occ; + STATS projection; + + blob_box = blob->bounding_box (); + blob_width = blob->bounding_box ().width (); + projection.set_range (blob_box.bottom (), blob_box.top () + 1); + if (testing_on) { + // blob->plot(to_win,GOLDENROD,GOLDENROD); + // line_color_index(to_win,GOLDENROD); + // move2d(to_win,blob_box.left(),baseline); + // draw2d(to_win,blob_box.right(),baseline); + // move2d(to_win,blob_box.left(),baseline+xheight); + // draw2d(to_win,blob_box.right(),baseline+xheight); + tprintf + ("Testing underline on blob at (%d,%d)->(%d,%d), base=%d\nOccs:", + blob->bounding_box ().left (), blob->bounding_box ().bottom (), + blob->bounding_box ().right (), blob->bounding_box ().top (), + baseline); + } + horizontal_cblob_projection(blob, &projection); + desc_occ = 0; + for (occ = blob_box.bottom (); occ < baseline; occ++) + if (occ <= blob_box.top () && projection.pile_count (occ) > desc_occ) + //max in region + desc_occ = projection.pile_count (occ); + x_occ = 0; + for (occ = baseline; occ <= baseline + xheight; occ++) + if (occ >= blob_box.bottom () && occ <= blob_box.top () + && projection.pile_count (occ) > x_occ) + //max in region + x_occ = projection.pile_count (occ); + asc_occ = 0; + for (occ = baseline + xheight + 1; occ <= blob_box.top (); occ++) + if (occ >= blob_box.bottom () && projection.pile_count (occ) > asc_occ) + asc_occ = projection.pile_count (occ); + if (testing_on) { + tprintf ("%d %d %d\n", desc_occ, x_occ, asc_occ); + } + if (desc_occ == 0 && x_occ == 0 && asc_occ == 0) { + tprintf ("Bottom=%d, top=%d, base=%d, x=%d\n", + blob_box.bottom (), blob_box.top (), baseline, xheight); + projection.print (stdout, TRUE); + } + if (desc_occ > x_occ + x_occ + && desc_occ > blob_width * textord_underline_threshold) + return TRUE; //real underline + if (asc_occ > x_occ + x_occ + && asc_occ > blob_width * textord_underline_threshold) + return TRUE; //overline + return FALSE; //neither +} + + +/********************************************************************** + * horizontal_cblob_projection + * + * Compute the horizontal projection of a cblob from its outlines + * and add to the given STATS. + **********************************************************************/ + +void horizontal_cblob_projection( //project outlines + C_BLOB *blob, //blob to project + STATS *stats //output + ) { + //outlines of blob + C_OUTLINE_IT out_it = blob->out_list (); + + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + horizontal_coutline_projection (out_it.data (), stats); + } +} + + +/********************************************************************** + * horizontal_coutline_projection + * + * Compute the horizontal projection of a outline from its outlines + * and add to the given STATS. + **********************************************************************/ + +void horizontal_coutline_projection( //project outlines + C_OUTLINE *outline, //outline to project + STATS *stats //output + ) { + ICOORD pos; //current point + ICOORD step; //edge step + INT32 length; //of outline + INT16 stepindex; //current step + C_OUTLINE_IT out_it = outline->child (); + + pos = outline->start_pos (); + length = outline->pathlength (); + for (stepindex = 0; stepindex < length; stepindex++) { + step = outline->step (stepindex); + if (step.y () > 0) { + stats->add (pos.y (), pos.x ()); + } + else if (step.y () < 0) { + stats->add (pos.y () - 1, -pos.x ()); + } + pos += step; + } + + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + horizontal_coutline_projection (out_it.data (), stats); + } +} + + +void set_bands( //init from varibles + float baseline, //top of bottom band + float xheight //height of split band + ) { + INT16 int_bl, int_xh; //for band.set + + bands[DOT_BAND].set (0, 0, 0, 0, 0, 0); + + int_bl = (INT16) baseline; + int_xh = (INT16) xheight; + bands[1].set (int_bl, int_bl, int_bl, + NO_LOWER_LIMIT, NO_LOWER_LIMIT, NO_LOWER_LIMIT); + + bands[2].set (int_bl + int_xh / 2, int_bl + int_xh / 2, int_bl + int_xh / 2, + int_bl, int_bl, int_bl); + + bands[3].set (int_bl + int_xh, int_bl + int_xh, int_bl + int_xh, + int_bl + int_xh / 2, int_bl + int_xh / 2, + int_bl + int_xh / 2); + + bands[4].set (NO_UPPER_LIMIT, NO_UPPER_LIMIT, NO_UPPER_LIMIT, + int_bl + int_xh, int_bl + int_xh, int_bl + int_xh); +} + + +void +block_occ (PBLOB * blob, //blob to do +float occs[] //output histogram +) { + int band_index; //current band + REGION_OCC *region; //current segment + REGION_OCC_LIST region_occ_list[MAX_NUM_BANDS + 1]; + REGION_OCC_IT region_it; //region iterator + + find_transitions(blob, region_occ_list); + compress_region_list(region_occ_list); + for (band_index = 0; band_index <= MAX_NUM_BANDS; band_index++) { + occs[band_index] = 0.0f; + region_it.set_to_list (®ion_occ_list[band_index]); + for (region_it.mark_cycle_pt (); !region_it.cycled_list (); + region_it.forward ()) { + region = region_it.data (); + occs[band_index] += region->max_x - region->min_x; + } + } +} + + +void find_transitions(PBLOB *blob, //blob to do + REGION_OCC_LIST *region_occ_list) { + OUTLINE_IT outline_it; + BOX box; + POLYPT_IT pt_it; + FCOORD point1; + FCOORD point2; + FCOORD *entry_pt = &point1; + FCOORD *exit_pt = &point2; + FCOORD *temp_pt; + INT16 increment; + INT16 prev_band; + INT16 band; + INT16 next_band; + float min_x; + float max_x; + float min_y; + float max_y; + BOOL8 doubly_contained; + + outline_it = blob->out_list (); + for (outline_it.mark_cycle_pt (); !outline_it.cycled_list (); + outline_it.forward ()) { + find_fbox(&outline_it, &min_x, &min_y, &max_x, &max_y); + + if (bands[DOT_BAND].range_in_nominal (max_y, min_y)) { + record_region(DOT_BAND, + min_x, + max_x, + REGION_TYPE_ENCLOSED, + region_occ_list); + } + else { + band = find_containing_maximal_band (max_y, min_y, + &doubly_contained); + if (band != UNDEFINED_BAND) { + //No transitions + if (!doubly_contained) + record_region(band, + min_x, + max_x, + REGION_TYPE_ENCLOSED, + region_occ_list); + else { + // if (wordocc_debug_on && blockocc_show_result) + // { + // fprintf( db_win, + // "Ignoring doubly contained outline (%d, %d) (%d, %d)\n", + // box.left(), box.top(), + // box.right(), box.bottom()); + // fprintf( db_win, "\tContained in bands %d and %d\n", + // band, band + 1 ); + // } + } + } + else { + //There are transitns + /* + Determining a good start point for recognising transitions between bands + is complicated by error limits on bands. We need to find a line which + significantly occupies a band. + + Having found such a point, we need to find a significant transition out of + its band and start the walk around the outline from there. + + Note that we are relying on having recognised and dealt with elsewhere, + outlines which do not significantly occupy more than one region. A + particularly nasty case of this are outlines which do not significantly + occupy ANY band. I.e. they lie entirely within the error limits. + Given this condition, all remaining outlines must contain at least one + significant line. */ + + pt_it = outline_it.data ()->polypts (); + + find_significant_line(pt_it, &band); + *entry_pt = pt_it.data ()->pos; + next_region(&pt_it, + band, + &next_band, + &min_x, + &max_x, + &increment, + exit_pt); + pt_it.mark_cycle_pt (); + + // Found the first real transition, so start walking the outline from here. + + do { + prev_band = band; + band = band + increment; + + while (band != next_band) { + temp_pt = entry_pt; + entry_pt = exit_pt; + exit_pt = temp_pt; + min_x = max_x = entry_pt->x (); + + find_trans_point (&pt_it, band, band + increment, + exit_pt); + maintain_limits (&min_x, &max_x, exit_pt->x ()); + + record_region (band, + min_x, + max_x, + find_region_type (prev_band, + band, + band + increment, + entry_pt->x (), + exit_pt->x ()), + region_occ_list); + prev_band = band; + band = band + increment; + } + + temp_pt = entry_pt; + entry_pt = exit_pt; + exit_pt = temp_pt; + min_x = max_x = entry_pt->x (); + next_region(&pt_it, + band, + &next_band, + &min_x, + &max_x, + &increment, + exit_pt); + + record_region (band, + min_x, + max_x, + find_region_type (prev_band, + band, + band + increment, + entry_pt->x (), + exit_pt->x ()), + region_occ_list); + } + while (!pt_it.cycled_list ()); + } + } + } +} + + +void record_region( //add region on list + INT16 band, + float new_min, + float new_max, + INT16 region_type, + REGION_OCC_LIST *region_occ_list) { + REGION_OCC_IT it (&(region_occ_list[band])); + + // if (wordocc_debug_on && blockocc_show_result) + // fprintf( db_win, "\nBand %d, region type %d, from %f to %f", + // band, region_type, new_min, new_max ); + + if ((region_type == REGION_TYPE_UPPER_UNBOUND) || + (region_type == REGION_TYPE_LOWER_UNBOUND) || + (region_type == REGION_TYPE_EMPTY)) + return; + + if (it.empty ()) { + it.add_after_stay_put (new REGION_OCC (new_min, new_max, region_type)); + } + else { + + /* Insert in sorted order of average limit */ + + while ((new_min + new_max > it.data ()->min_x + it.data ()->max_x) && + (!it.at_last ())) + it.forward (); + + if ((it.at_last ()) && //at the end + (new_min + new_max > it.data ()->min_x + it.data ()->max_x)) + //new range > current + it.add_after_stay_put (new REGION_OCC (new_min, + new_max, region_type)); + else { + it.add_before_stay_put (new REGION_OCC (new_min, + new_max, region_type)); + } + } +} + + +INT16 find_containing_maximal_band( //find range's band + float y1, + float y2, + BOOL8 *doubly_contained) { + INT16 band; + + *doubly_contained = FALSE; + + for (band = 1; band <= blockocc_band_count; band++) { + if (bands[band].range_in_maximal (y1, y2)) { + if ((band < blockocc_band_count) && + (bands[band + 1].range_in_maximal (y1, y2))) + *doubly_contained = TRUE; + return band; + } + } + return UNDEFINED_BAND; +} + + +void find_significant_line(POLYPT_IT it, INT16 *band) { + + /* Look for a line which significantly occupies at least one band. I.e. part + of the line is in the non-margin part of the band. */ + + *band = find_overlapping_minimal_band (it.data ()->pos.y (), + it.data ()->pos.y () + + it.data ()->vec.y ()); + + while (*band == UNDEFINED_BAND) { + it.forward (); + *band = find_overlapping_minimal_band (it.data ()->pos.y (), + it.data ()->pos.y () + + it.data ()->vec.y ()); + } +} + + +INT16 find_overlapping_minimal_band( //find range's band + float y1, + float y2) { + INT16 band; + + for (band = 1; band <= blockocc_band_count; band++) { + if (bands[band].range_overlaps_minimal (y1, y2)) + return band; + } + return UNDEFINED_BAND; +} + + +INT16 find_region_type(INT16 entry_band, + INT16 current_band, + INT16 exit_band, + float entry_x, + float exit_x) { + if (entry_band > exit_band) + return REGION_TYPE_OPEN_RIGHT; + if (entry_band < exit_band) + return REGION_TYPE_OPEN_LEFT; + if (entry_x == exit_x) + return REGION_TYPE_EMPTY; + if (entry_band > current_band) { + if (entry_x < exit_x) + return REGION_TYPE_UPPER_BOUND; + else + return REGION_TYPE_UPPER_UNBOUND; + } + else { + if (entry_x > exit_x) + return REGION_TYPE_LOWER_BOUND; + else + return REGION_TYPE_LOWER_UNBOUND; + } +} + + +void find_trans_point(POLYPT_IT *pt_it, + INT16 current_band, + INT16 next_band, + FCOORD *transition_pt) { + float x1, x2, y1, y2; // points of edge + float gradient; // m in y = mx + c + float offset; // c in y = mx + c + + if (current_band < next_band) + transition_pt->set_y (bands[current_band].max); + //going up + else + transition_pt->set_y (bands[current_band].min); + //going down + + x1 = pt_it->data ()->pos.x (); + x2 = x1 + pt_it->data ()->vec.x (); + y1 = pt_it->data ()->pos.y (); + y2 = y1 + pt_it->data ()->vec.y (); + + if (x1 == x2) + transition_pt->set_x (x1); //avoid div by 0 + else { + if (y1 == y2) //avoid div by 0 + transition_pt->set_x ((x1 + x2) / 2.0); + else { + gradient = (y1 - y2) / (float) (x1 - x2); + offset = y1 - x1 * gradient; + transition_pt->set_x ((transition_pt->y () - offset) / gradient); + } + } +} + + +void next_region(POLYPT_IT *start_pt_it, + INT16 start_band, + INT16 *to_band, + float *min_x, + float *max_x, + INT16 *increment, + FCOORD *exit_pt) { + /* + Given an edge and a band which the edge significantly occupies, find the + significant end of the region containing the band. I.e. find an edge which + points to another band such that the outline subsequetly moves significantly + out of the starting band. + + Note that we can assume that we are significantly inside the current band to + start with because the edges passed will be from previous calls to this + routine apart from the first - the result of which is only used to establish + the start of the first region. + */ + + INT16 band; //band of current edge + INT16 prev_band = start_band; //band of prev edge + //edge crossing out + POLYPT_IT last_transition_out_it; + //band it pts to + INT16 last_trans_out_to_band = 0; + float ext_min_x = 0.0f; + float ext_max_x = 0.0f; + + start_pt_it->forward (); + band = find_band (start_pt_it->data ()->pos.y ()); + + while ((band == start_band) || + bands[start_band].in_maximal (start_pt_it->data ()->pos.y ())) { + if (band == start_band) { + //Return to start band + if (prev_band != start_band) { + *min_x = ext_min_x; + *max_x = ext_max_x; + } + maintain_limits (min_x, max_x, start_pt_it->data ()->pos.x ()); + } + else { + if (prev_band == start_band) { + //Exit from start band + //so remember edge + last_transition_out_it = *start_pt_it; + //before we left + last_transition_out_it.backward (); + //and band it pts to + last_trans_out_to_band = band; + ext_min_x = *min_x; + ext_max_x = *max_x; + } + maintain_limits (&ext_min_x, &ext_max_x, + start_pt_it->data ()->pos.x ()); + } + prev_band = band; + start_pt_it->forward (); + band = find_band (start_pt_it->data ()->pos.y ()); + } + + if (prev_band == start_band) { //exit from start band + *to_band = band; + //so remember edge + last_transition_out_it = *start_pt_it; + //before we left + last_transition_out_it.backward (); + } + else { + *to_band = last_trans_out_to_band; + } + + if (*to_band > start_band) + *increment = 1; + else + *increment = -1; + + find_trans_point (&last_transition_out_it, start_band, + start_band + *increment, exit_pt); + maintain_limits (min_x, max_x, exit_pt->x ()); + *start_pt_it = last_transition_out_it; +} + + +INT16 find_band( // find POINT's band + float y) { + INT16 band; + + for (band = 1; band <= blockocc_band_count; band++) { + if (bands[band].in_nominal (y)) + return band; + } + BLOCKOCC.error ("find_band", ABORT, "Cant find band for %d", y); + return 0; +} + + +void compress_region_list( // join open regions + REGION_OCC_LIST *region_occ_list) { + REGION_OCC_IT it (&(region_occ_list[0])); + REGION_OCC *open_right = NULL; + + INT16 i = 0; + + for (i = 0; i <= blockocc_band_count; i++) { + it.set_to_list (&(region_occ_list[i])); + if (!it.empty ()) { + /* First check for left right pairs. Merge them into the open right and delete + the open left. */ + open_right = NULL; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + switch (it.data ()->region_type) { + case REGION_TYPE_OPEN_RIGHT: + { + if (open_right != NULL) + BLOCKOCC.error ("compress_region_list", ABORT, + "unmatched right"); + else + open_right = it.data (); + break; + } + case REGION_TYPE_OPEN_LEFT: + { + if (open_right == NULL) + BLOCKOCC.error ("compress_region_list", ABORT, + "unmatched left"); + else { + open_right->max_x = it.data ()->max_x; + open_right = NULL; + delete it.extract (); + } + break; + } + default: + break; + } + } + if (open_right != NULL) + BLOCKOCC.error ("compress_region_list", ABORT, + "unmatched right remaining"); + + /* Now cycle the list again, merging and deleting any redundant regions */ + it.move_to_first (); + open_right = it.data (); + while (!it.at_last ()) { + it.forward (); + if (it.data ()->min_x <= open_right->max_x) { + // Overlaps + if (it.data ()->max_x > open_right->max_x) + open_right->max_x = it.data ()->max_x; + // Extend + delete it.extract (); + } + else + open_right = it.data (); + } + } + } +} + + +void find_fbox(OUTLINE_IT *out_it, + float *min_x, + float *min_y, + float *max_x, + float *max_y) { + POLYPT_IT pt_it = out_it->data ()->polypts (); + FCOORD pt; + *min_x = 9999.0f; + *min_y = 9999.0f; + *max_x = 0.0f; + *max_y = 0.0f; + + for (pt_it.mark_cycle_pt (); !pt_it.cycled_list (); pt_it.forward ()) { + pt = pt_it.data ()->pos; + maintain_limits (min_x, max_x, pt.x ()); + maintain_limits (min_y, max_y, pt.y ()); + } +} + + +void maintain_limits(float *min_x, float *max_x, float x) { + if (x > *max_x) + *max_x = x; + if (x < *min_x) + *min_x = x; +} diff --git a/textord/blkocc.h b/textord/blkocc.h new file mode 100644 index 0000000000..699e9e1812 --- /dev/null +++ b/textord/blkocc.h @@ -0,0 +1,327 @@ +/****************************************************************************** + * + * File: blkocc.h (Formerly blockocc.h) + * Description: Block Occupancy routines + * Author: Chris Newton + * Created: Fri Nov 8 + * Modified: + * Language: C++ + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1991, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************/ + +#ifndef BLKOCC_H +#define BLKOCC_H + +#include "varable.h" +#include "polyblob.h" +#include "elst.h" +#include "notdll.h" +#include "notdll.h" + +/*************************************************************************** +CLASS REGION_OCC + + The class REGION_OCC defines a section of outline which exists entirely + within a single region. The only data held is the min and max x limits of + the outline within the region. + + REGION_OCCs are held on lists, one list for each region. The lists are + built in sorted order of min x. Overlapping REGION_OCCs are not permitted on + a single list. An overlapping region to be added causes the existing region + to be extended. This extension may result in the following REGION_OCC on the + list overlapping the ammended one. In this case the ammended REGION_OCC is + further extended to include the range of the following one, so that the + following one can be deleted. + +****************************************************************************/ + +class REGION_OCC:public ELIST_LINK +{ + public: + float min_x; //Lowest x in region + float max_x; //Highest x in region + INT16 region_type; //Type of crossing + + REGION_OCC() { + }; //constructor used + //only in COPIER etc + REGION_OCC( //constructor + float min, + float max, + INT16 region) { + min_x = min; + max_x = max; + region_type = region; + } +}; + +ELISTIZEH (REGION_OCC) +#define RANGE_IN_BAND( band_max, band_min, range_max, range_min ) \ +( ((range_min) >= (band_min)) && ((range_max) < (band_max)) ) ? TRUE : FALSE +/************************************************************************ +Adapted from the following procedure so that it can be used in the bands +class in an include file... + +BOOL8 range_in_band[ + range within band? +INT16 band_max, +INT16 band_min, +INT16 range_max, +INT16 range_min] +{ + if ( (range_min >= band_min) && (range_max < band_max) ) + return TRUE; + else + return FALSE; +} +***********************************************************************/ +#define RANGE_OVERLAPS_BAND( band_max, band_min, range_max, range_min ) \ +( ((range_max) >= (band_min)) && ((range_min) < (band_max)) ) ? TRUE : FALSE +/************************************************************************ +Adapted from the following procedure so that it can be used in the bands +class in an include file... + +BOOL8 range_overlaps_band[ + range crosses band? +INT16 band_max, +INT16 band_min, +INT16 range_max, +INT16 range_min] +{ + if ( (range_max >= band_min) && (range_min < band_max) ) + return TRUE; + else + return FALSE; +} +***********************************************************************/ +/********************************************************************** + Bands + ----- + + BAND 4 +-------------------------------- + BAND 3 +-------------------------------- + + BAND 2 + +-------------------------------- + + BAND 1 + +Band 0 is the dot band + +Each band has an error margin above and below. An outline is not considered to +have significantly changed bands until it has moved out of the error margin. +*************************************************************************/ +class BAND +{ + public: + INT16 max_max; //upper max + INT16 max; //nominal max + INT16 min_max; //lower max + INT16 max_min; //upper min + INT16 min; //nominal min + INT16 min_min; //lower min + + BAND() { + } // constructor + + void set( // initialise a band + INT16 new_max_max, // upper max + INT16 new_max, // new nominal max + INT16 new_min_max, // new lower max + INT16 new_max_min, // new upper min + INT16 new_min, // new nominal min + INT16 new_min_min) { // new lower min + max_max = new_max_max; + max = new_max; + min_max = new_min_max; + max_min = new_max_min; + min = new_min; + min_min = new_min_min; + } + + BOOL8 in_minimal( //in minimal limits? + float y) { //y value + if ((y >= max_min) && (y < min_max)) + return TRUE; + else + return FALSE; + } + + BOOL8 in_nominal( //in nominal limits? + float y) { //y value + if ((y >= min) && (y < max)) + return TRUE; + else + return FALSE; + } + + BOOL8 in_maximal( //in maximal limits? + float y) { //y value + if ((y >= min_min) && (y < max_max)) + return TRUE; + else + return FALSE; + } + + //overlaps min limits? + BOOL8 range_overlaps_minimal(float y1, //one range limit + float y2) { //other range limit + if (y1 > y2) + return RANGE_OVERLAPS_BAND (min_max, max_min, y1, y2); + else + return RANGE_OVERLAPS_BAND (min_max, max_min, y2, y1); + } + + //overlaps nom limits? + BOOL8 range_overlaps_nominal(float y1, //one range limit + float y2) { //other range limit + if (y1 > y2) + return RANGE_OVERLAPS_BAND (max, min, y1, y2); + else + return RANGE_OVERLAPS_BAND (max, min, y2, y1); + } + + //overlaps max limits? + BOOL8 range_overlaps_maximal(float y1, //one range limit + float y2) { //other range limit + if (y1 > y2) + return RANGE_OVERLAPS_BAND (max_max, min_min, y1, y2); + else + return RANGE_OVERLAPS_BAND (max_max, min_min, y2, y1); + } + + BOOL8 range_in_minimal( //within min limits? + float y1, //one range limit + float y2) { //other range limit + if (y1 > y2) + return RANGE_IN_BAND (min_max, max_min, y1, y2); + else + return RANGE_IN_BAND (min_max, max_min, y2, y1); + } + + BOOL8 range_in_nominal( //within nom limits? + float y1, //one range limit + float y2) { //other range limit + if (y1 > y2) + return RANGE_IN_BAND (max, min, y1, y2); + else + return RANGE_IN_BAND (max, min, y2, y1); + } + + BOOL8 range_in_maximal( //within max limits? + float y1, //one range limit + float y2) { //other range limit + if (y1 > y2) + return RANGE_IN_BAND (max_max, min_min, y1, y2); + else + return RANGE_IN_BAND (max_max, min_min, y2, y1); + } +}; + +/* Standard positions */ + +#define MAX_NUM_BANDS 5 +#define UNDEFINED_BAND 99 +#define NO_LOWER_LIMIT -9999 +#define NO_UPPER_LIMIT 9999 + +#define DOT_BAND 0 + +/* Special occupancy code emitted for the 0 region at the end of a word */ + +#define END_OF_WERD_CODE 255 + +extern BOOL_VAR_H (blockocc_show_result, FALSE, "Show intermediate results"); +extern INT_VAR_H (blockocc_desc_height, 0, +"Descender height after normalisation"); +extern INT_VAR_H (blockocc_asc_height, 255, +"Ascender height after normalisation"); +extern INT_VAR_H (blockocc_band_count, 4, "Number of bands used"); +extern double_VAR_H (textord_underline_threshold, 0.9, +"Fraction of width occupied"); +BOOL8 test_underline( //look for underlines + BOOL8 testing_on, //drawing blob + PBLOB *blob, //blob to test + float baseline, //coords of baseline + float xheight //height of line + ); +BOOL8 test_underline( //look for underlines + BOOL8 testing_on, //drawing blob + C_BLOB *blob, //blob to test + INT16 baseline, //coords of baseline + INT16 xheight //height of line + ); + //project outlines +void horizontal_cblob_projection(C_BLOB *blob, //blob to project + STATS *stats //output + ); +void horizontal_coutline_projection( //project outlines + C_OUTLINE *outline, //outline to project + STATS *stats //output + ); +void set_bands( //init from varibles + float baseline, //top of bottom band + float xheight //height of split band + ); +void block_occ (PBLOB * blob, //blob to do +float occs[] //output histogram +); + //blob to do +void find_transitions(PBLOB *blob, REGION_OCC_LIST *region_occ_list); +void record_region( //add region on list + INT16 band, + float new_min, + float new_max, + INT16 region_type, + REGION_OCC_LIST *region_occ_list); +INT16 find_containing_maximal_band( //find range's band + float y1, + float y2, + BOOL8 *doubly_contained); +void find_significant_line(POLYPT_IT it, INT16 *band); +INT16 find_overlapping_minimal_band( //find range's band + float y1, + float y2); +INT16 find_region_type(INT16 entry_band, + INT16 current_band, + INT16 exit_band, + float entry_x, + float exit_x); +void find_trans_point(POLYPT_IT *pt_it, + INT16 current_band, + INT16 next_band, + FCOORD *transition_pt); +void next_region(POLYPT_IT *start_pt_it, + INT16 start_band, + INT16 *to_band, + float *min_x, + float *max_x, + INT16 *increment, + FCOORD *exit_pt); +INT16 find_band( // find POINT's band + float y); +void compress_region_list( // join open regions + REGION_OCC_LIST *region_occ_list); +void find_fbox(OUTLINE_IT *out_it, + float *min_x, + float *min_y, + float *max_x, + float *max_y); +void maintain_limits(float *min_x, float *max_x, float x); +#endif diff --git a/textord/blobcmpl.h b/textord/blobcmpl.h new file mode 100644 index 0000000000..05869f0392 --- /dev/null +++ b/textord/blobcmpl.h @@ -0,0 +1,32 @@ +/********************************************************************** + * File: blobcmpl.h (Formerly paircmpl.h) + * Description: Code to compare two blobs using the adaptive matcher + * Author: Ray Smith + * Created: Wed Apr 21 09:31:02 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef BLOBCMPL_H +#define BLOBCMPL_H + +class PBLOB; +class ROW; +#define BLOB_MATCHING_ON + +float compare_blobs( //match 2 blobs + PBLOB *blob1, //first blob + ROW *row1, //row it came from + PBLOB *blob2, //other blob + ROW *row2); +#endif diff --git a/textord/drawedg.cpp b/textord/drawedg.cpp new file mode 100644 index 0000000000..6fdf8ac752 --- /dev/null +++ b/textord/drawedg.cpp @@ -0,0 +1,79 @@ +/********************************************************************** + * File: drawedg.c (Formerly drawedge.c) + * Description: Collection of functions to draw things to do with edge detection. + * Author: Ray Smith + * Created: Thu Jun 06 13:29:20 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "drawedg.h" +#include "evnts.h" + +#define IMAGE_WIN_NAME "Edges"//title of window +#define IMAGE_XPOS 250 +#define IMAGE_YPOS 0 //default position +#define CTRLD '\004' //control D + +#define EXTERN + +/********************************************************************** + * create_edges_window + * + * Create the edges window. + **********************************************************************/ + +WINDOW create_edges_window( //make window + ICOORD page_tr //size of image + ) { + WINDOW image_win; //image window + + //create the window + image_win = create_window (IMAGE_WIN_NAME, SCROLLINGWIN, IMAGE_XPOS, IMAGE_YPOS, 0, 0, (float) 0.0, page_tr.x (), (float) 0.0, page_tr.y (), TRUE, FALSE, FALSE, FALSE); + #ifdef __MSW32__ + set_selection_handler(image_win, win_selection_handler); + #endif + overlap_picture_ops(TRUE); + return image_win; //window +} + + +/********************************************************************** + * draw_raw_edge + * + * Draw the raw steps to the given window in the given colour. + **********************************************************************/ + +void draw_raw_edge( //draw the cracks + WINDOW fd, //window to draw in + CRACKEDGE *start, //start of loop + COLOUR colour //colour to draw in + ) { + CRACKEDGE *edgept; //current point + + line_color_index(fd, colour); //in white + edgept = start; + move2d (fd, edgept->pos.x (), edgept->pos.y ()); + do { + do + edgept = edgept->next; + //merge straight lines + while (edgept != start && edgept->prev->stepx == edgept->stepx && edgept->prev->stepy == edgept->stepy); + + //draw lines + draw2d (fd, edgept->pos.x (), edgept->pos.y ()); + } + while (edgept != start); +} + diff --git a/textord/drawedg.h b/textord/drawedg.h new file mode 100644 index 0000000000..fc71aef088 --- /dev/null +++ b/textord/drawedg.h @@ -0,0 +1,34 @@ +/********************************************************************** + * File: drawedg.h (Formerly drawedge.h) + * Description: Collection of functions to draw things to do with edge detection. + * Author: Ray Smith + * Created: Thu Jun 06 13:29:20 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef DRAWEDG_H +#define DRAWEDG_H + +#include "grphics.h" +#include "crakedge.h" + +WINDOW create_edges_window( //make window + ICOORD page_tr //size of image + ); +void draw_raw_edge( //draw the cracks + WINDOW fd, //window to draw in + CRACKEDGE *start, //start of loop + COLOUR colour //colour to draw in + ); +#endif diff --git a/textord/drawtord.cpp b/textord/drawtord.cpp new file mode 100644 index 0000000000..3331803e26 --- /dev/null +++ b/textord/drawtord.cpp @@ -0,0 +1,475 @@ +/********************************************************************** + * File: drawtord.cpp (Formerly drawto.c) + * Description: Draw things to do with textord. + * Author: Ray Smith + * Created: Thu Jul 30 15:40:57 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "pithsync.h" +#include "topitch.h" +#include "drawtord.h" +#include "debugwin.h" + +#define TO_WIN_XPOS -1 //default window pos +#define TO_WIN_YPOS 0 +#define TO_WIN_NAME "Textord" + //title of window +#define DEBUG_WIN_NAME "TODebug" +#define DEBUG_XPOS 0 +#define DEBUG_YPOS 120 +#define DEBUG_XSIZE 80 +#define DEBUG_YSIZE 32 +#define YMAX 3508 +#define XMAX 2550 + +#define EXTERN + +EXTERN BOOL_VAR (textord_show_fixed_cuts, FALSE, +"Draw fixed pitch cell boundaries"); +EXTERN STRING_VAR (to_debugfile, DEBUG_WIN_NAME, "Name of debugfile"); +EXTERN STRING_VAR (to_smdfile, NO_SMD, "Name of SMD file"); + +EXTERN WINDOW to_win = NO_WINDOW; +EXTERN FILE *to_debug = NULL; + +/********************************************************************** + * create_to_win + * + * Create the to window used to show the fit. + **********************************************************************/ + +void create_to_win( //make features win + ICOORD page_tr //size of page + ) { + if (strcmp (to_smdfile.string (), NO_SMD)) { + to_win = create_window (to_smdfile.string (), SMDWINDOW, + 0, 0, page_tr.x () + 1, page_tr.y () + 1, + 0.0, page_tr.x (), 0.0, page_tr.y (), + TRUE, FALSE, TRUE, TRUE); + } + else { + to_win = create_window (TO_WIN_NAME, SCROLLINGWIN, + TO_WIN_XPOS, TO_WIN_YPOS, 0, 0, + 0.0, page_tr.x (), 0.0, page_tr.y (), + TRUE, FALSE, TRUE, TRUE); + } +} + + +void close_to_win() { //make features win + if (to_win != NO_WINDOW && strcmp (to_smdfile.string (), NO_SMD)) { + destroy_window(to_win); + overlap_picture_ops(TRUE); + } +} + + +/********************************************************************** + * create_todebug_win + * + * Create the to window used to show the fit. + **********************************************************************/ + +void create_todebug_win() { //make gradients win + if (strcmp (to_debugfile.string (), DEBUG_WIN_NAME) != 0) + // create_debug_window(); + // else + to_debug = fopen (to_debugfile.string (), "w"); +} + + +/********************************************************************** + * plot_blob_list + * + * Draw a list of blobs. + **********************************************************************/ + +void plot_blob_list( //make gradients win + WINDOW win, //window to draw in + BLOBNBOX_LIST *list, //blob list + COLOUR body_colour, //colour to draw + COLOUR child_colour //colour of child + ) { + BLOBNBOX_IT it = list; //iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (win, body_colour, child_colour); + } +} + + +/********************************************************************** + * plot_box_list + * + * Draw a list of blobs. + **********************************************************************/ + +void plot_box_list( //make gradients win + WINDOW win, //window to draw in + BLOBNBOX_LIST *list, //blob list + COLOUR body_colour //colour to draw + ) { + BLOBNBOX_IT it = list; //iterator + + perimeter_color_index(win, body_colour); + interior_style(win, INT_HOLLOW, TRUE); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->bounding_box ().plot (win); + } +} + + +/********************************************************************** + * plot_to_row + * + * Draw the blobs of a row in a given colour and draw the line fit. + **********************************************************************/ + +void plot_to_row( //draw a row + TO_ROW *row, //row to draw + COLOUR colour, //colour to draw in + FCOORD rotation //rotation for line + ) { + FCOORD plot_pt; //point to plot + //blobs + BLOBNBOX_IT it = row->blob_list (); + float left, right; //end of row + + if (it.empty ()) { + tprintf ("No blobs in row at %g\n", row->parallel_c ()); + return; + } + left = it.data ()->bounding_box ().left (); + it.move_to_last (); + right = it.data ()->bounding_box ().right (); + plot_blob_list (to_win, row->blob_list (), colour, BROWN); + line_color_index(to_win, colour); + plot_pt = FCOORD (left, row->line_m () * left + row->line_c ()); + plot_pt.rotate (rotation); + move2d (to_win, plot_pt.x (), plot_pt.y ()); + plot_pt = FCOORD (right, row->line_m () * right + row->line_c ()); + plot_pt.rotate (rotation); + draw2d (to_win, plot_pt.x (), plot_pt.y ()); +} + + +/********************************************************************** + * plot_parallel_row + * + * Draw the blobs of a row in a given colour and draw the line fit. + **********************************************************************/ + +void plot_parallel_row( //draw a row + TO_ROW *row, //row to draw + float gradient, //gradients of lines + INT32 left, //edge of block + COLOUR colour, //colour to draw in + FCOORD rotation //rotation for line + ) { + FCOORD plot_pt; //point to plot + //blobs + BLOBNBOX_IT it = row->blob_list (); + float fleft = (float) left; //floating version + float right; //end of row + + // left=it.data()->bounding_box().left(); + it.move_to_last (); + right = it.data ()->bounding_box ().right (); + plot_blob_list (to_win, row->blob_list (), colour, BROWN); + line_color_index(to_win, colour); + plot_pt = FCOORD (fleft, gradient * left + row->max_y ()); + plot_pt.rotate (rotation); + move2d (to_win, plot_pt.x (), plot_pt.y ()); + plot_pt = FCOORD (fleft, gradient * left + row->min_y ()); + plot_pt.rotate (rotation); + draw2d (to_win, plot_pt.x (), plot_pt.y ()); + plot_pt = FCOORD (fleft, gradient * left + row->parallel_c ()); + plot_pt.rotate (rotation); + move2d (to_win, plot_pt.x (), plot_pt.y ()); + plot_pt = FCOORD (right, gradient * right + row->parallel_c ()); + plot_pt.rotate (rotation); + draw2d (to_win, plot_pt.x (), plot_pt.y ()); +} + + +/********************************************************************** + * draw_occupation + * + * Draw the row occupation with points above the threshold in white + * and points below the threshold in black. + **********************************************************************/ + +void +draw_occupation ( //draw projection +INT32 xleft, //edge of block +INT32 ybottom, //bottom of block +INT32 min_y, //coordinate limits +INT32 max_y, INT32 occupation[], //projection counts +INT32 thresholds[] //for drop out +) { + INT32 line_index; //pixel coord + COLOUR colour; //of histogram + float fleft = (float) xleft; //float version + + colour = WHITE; + line_color_index(to_win, colour); + move2d (to_win, fleft, (float) ybottom); + for (line_index = min_y; line_index <= max_y; line_index++) { + if (occupation[line_index - min_y] < thresholds[line_index - min_y]) { + if (colour != BLUE) { + colour = BLUE; + line_color_index(to_win, colour); + } + } + else { + if (colour != WHITE) { + colour = WHITE; + line_color_index(to_win, colour); + } + } + draw2d (to_win, fleft + occupation[line_index - min_y] / 10.0, + (float) line_index); + } + line_color_index(to_win, STEEL_BLUE); + move2d (to_win, fleft, (float) ybottom); + for (line_index = min_y; line_index <= max_y; line_index++) { + draw2d (to_win, fleft + thresholds[line_index - min_y] / 10.0, + (float) line_index); + } +} + + +/********************************************************************** + * draw_meanlines + * + * Draw the meanlines of the given block in the given colour. + **********************************************************************/ + +void draw_meanlines( //draw a block + TO_BLOCK *block, //block to draw + float gradient, //gradients of lines + INT32 left, //edge of block + COLOUR colour, //colour to draw in + FCOORD rotation //rotation for line + ) { + FCOORD plot_pt; //point to plot + //rows + TO_ROW_IT row_it = block->get_rows (); + TO_ROW *row; //current row + BLOBNBOX_IT blob_it; //blobs + float right; //end of row + + line_color_index(to_win, colour); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + blob_it.set_to_list (row->blob_list ()); + blob_it.move_to_last (); + right = blob_it.data ()->bounding_box ().right (); + plot_pt = + FCOORD ((float) left, + gradient * left + row->parallel_c () + row->xheight); + plot_pt.rotate (rotation); + move2d (to_win, plot_pt.x (), plot_pt.y ()); + plot_pt = + FCOORD ((float) right, + gradient * right + row->parallel_c () + row->xheight); + plot_pt.rotate (rotation); + draw2d (to_win, plot_pt.x (), plot_pt.y ()); + } +} + + +/********************************************************************** + * plot_word_decisions + * + * Plot a row with words in different colours and fuzzy spaces + * highlighted. + **********************************************************************/ + +void plot_word_decisions( //draw words + WINDOW win, //window tro draw in + INT16 pitch, //of block + TO_ROW *row //row to draw + ) { + COLOUR colour = MAGENTA; //current colour + COLOUR rect_colour; //fuzzy colour + INT32 prev_x; //end of prev blob + INT16 blob_count; //blobs in word + BLOBNBOX *blob; //current blob + BOX blob_box; //bounding box + //iterator + BLOBNBOX_IT blob_it = row->blob_list (); + BLOBNBOX_IT start_it = blob_it;//word start + + interior_style(win, INT_SOLID, FALSE); + rect_colour = BLACK; + prev_x = -MAX_INT16; + blob_count = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + if (!blob->joined_to_prev () + && blob_box.left () - prev_x > row->max_nonspace) { + if ((blob_box.left () - prev_x >= row->min_space + || blob_box.left () - prev_x > row->space_threshold) + && blob_count > 0) { + if (pitch > 0 && textord_show_fixed_cuts) + plot_fp_cells (win, colour, &start_it, pitch, blob_count, + &row->projection, row->projection_left, + row->projection_right, + row->xheight * textord_projection_scale); + blob_count = 0; + start_it = blob_it; + } + if (colour == MAGENTA) + colour = RED; + else + colour = (COLOUR) (colour + 1); + if (blob_box.left () - prev_x < row->min_space) { + if (blob_box.left () - prev_x > row->space_threshold) + rect_colour = GOLDENROD; + else + rect_colour = CORAL; + fill_color_index(win, rect_colour); + rectangle (win, (float) prev_x, blob_box.bottom (), + blob_box.left (), blob_box.top ()); + } + } + if (!blob->joined_to_prev ()) + prev_x = blob_box.right (); + if (blob->blob () != NULL) + //draw it + blob->blob ()->plot (win, colour, colour); + if (blob->cblob () != NULL) + blob->cblob ()->plot (win, colour, colour); + if (!blob->joined_to_prev () + && (blob->blob () != NULL || blob->cblob () != NULL)) + blob_count++; + } + if (pitch > 0 && textord_show_fixed_cuts && blob_count > 0) + plot_fp_cells (win, colour, &start_it, pitch, blob_count, + &row->projection, row->projection_left, + row->projection_right, + row->xheight * textord_projection_scale); +} + + +/********************************************************************** + * plot_fp_cells + * + * Make a list of fixed pitch cuts and draw them. + **********************************************************************/ + +void plot_fp_cells( //draw words + WINDOW win, //window tro draw in + COLOUR colour, //colour of lines + BLOBNBOX_IT *blob_it, //blobs + INT16 pitch, //of block + INT16 blob_count, //no of real blobs + STATS *projection, //vertical + INT16 projection_left, //edges //scale factor + INT16 projection_right, + float projection_scale) { + INT16 occupation; //occupied cells + BOX word_box; //bounding box + FPSEGPT_LIST seg_list; //list of cuts + FPSEGPT_IT seg_it; + FPSEGPT *segpt; //current point + + if (pitsync_linear_version) + check_pitch_sync2 (blob_it, blob_count, pitch, 2, projection, + projection_left, projection_right, + projection_scale, occupation, &seg_list, 0, 0); + else + check_pitch_sync (blob_it, blob_count, pitch, 2, projection, &seg_list); + word_box = blob_it->data ()->bounding_box (); + for (; blob_count > 0; blob_count--) + word_box += box_next (blob_it); + seg_it.set_to_list (&seg_list); + for (seg_it.mark_cycle_pt (); !seg_it.cycled_list (); seg_it.forward ()) { + segpt = seg_it.data (); + if (segpt->faked) + line_color_index(win, WHITE); + else + line_color_index(win, colour); + move2d (win, segpt->position (), word_box.bottom ()); + draw2d (win, segpt->position (), word_box.top ()); + } +} + + +/********************************************************************** + * plot_fp_cells2 + * + * Make a list of fixed pitch cuts and draw them. + **********************************************************************/ + +void plot_fp_cells2( //draw words + WINDOW win, //window tro draw in + COLOUR colour, //colour of lines + TO_ROW *row, //for location + FPSEGPT_LIST *seg_list //segments to plot + ) { + BOX word_box; //bounding box + FPSEGPT_IT seg_it = seg_list; + //blobs in row + BLOBNBOX_IT blob_it = row->blob_list (); + FPSEGPT *segpt; //current point + + word_box = blob_it.data ()->bounding_box (); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();) + word_box += box_next (&blob_it); + for (seg_it.mark_cycle_pt (); !seg_it.cycled_list (); seg_it.forward ()) { + segpt = seg_it.data (); + if (segpt->faked) + line_color_index(win, WHITE); + else + line_color_index(win, colour); + move2d (win, segpt->position (), word_box.bottom ()); + draw2d (win, segpt->position (), word_box.top ()); + } +} + + +/********************************************************************** + * plot_row_cells + * + * Make a list of fixed pitch cuts and draw them. + **********************************************************************/ + +void plot_row_cells( //draw words + WINDOW win, //window tro draw in + COLOUR colour, //colour of lines + TO_ROW *row, //for location + float xshift, //amount of shift + ICOORDELT_LIST *cells //cells to draw + ) { + BOX word_box; //bounding box + ICOORDELT_IT cell_it = cells; + //blobs in row + BLOBNBOX_IT blob_it = row->blob_list (); + ICOORDELT *cell; //current cell + + word_box = blob_it.data ()->bounding_box (); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();) + word_box += box_next (&blob_it); + line_color_index(win, colour); + for (cell_it.mark_cycle_pt (); !cell_it.cycled_list (); cell_it.forward ()) { + cell = cell_it.data (); + move2d (win, cell->x () + xshift, word_box.bottom ()); + draw2d (win, cell->x () + xshift, word_box.top ()); + } +} diff --git a/textord/drawtord.h b/textord/drawtord.h new file mode 100644 index 0000000000..6c9fa90ff9 --- /dev/null +++ b/textord/drawtord.h @@ -0,0 +1,107 @@ +/********************************************************************** + * File: drawtord.h (Formerly drawto.h) + * Description: Draw things to do with textord. + * Author: Ray Smith + * Created: Thu Jul 30 15:40:57 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef DRAWTORD_H +#define DRAWTORD_H + +#include "varable.h" +#include "grphics.h" +#include "pitsync1.h" +#include "blobbox.h" +#include "notdll.h" + +#define NO_SMD "none" + +extern BOOL_VAR_H (textord_show_fixed_cuts, FALSE, +"Draw fixed pitch cell boundaries"); +extern STRING_VAR_H (to_debugfile, DEBUG_WIN_NAME, "Name of debugfile"); +extern STRING_VAR_H (to_smdfile, NO_SMD, "Name of SMD file"); +extern WINDOW to_win; +extern FILE *to_debug; +void create_to_win( //make features win + ICOORD page_tr //size of page + ); +void close_to_win(); //make features win +void create_todebug_win(); //make gradients win +void plot_blob_list( //make gradients win + WINDOW win, //window to draw in + BLOBNBOX_LIST *list, //blob list + COLOUR body_colour, //colour to draw + COLOUR child_colour //colour of child + ); +void plot_box_list( //make gradients win + WINDOW win, //window to draw in + BLOBNBOX_LIST *list, //blob list + COLOUR body_colour //colour to draw + ); +void plot_to_row( //draw a row + TO_ROW *row, //row to draw + COLOUR colour, //colour to draw in + FCOORD rotation //rotation for line + ); +void plot_parallel_row( //draw a row + TO_ROW *row, //row to draw + float gradient, //gradients of lines + INT32 left, //edge of block + COLOUR colour, //colour to draw in + FCOORD rotation //rotation for line + ); +void draw_occupation ( //draw projection +INT32 xleft, //edge of block +INT32 ybottom, //bottom of block +INT32 min_y, //coordinate limits +INT32 max_y, INT32 occupation[], //projection counts +INT32 thresholds[] //for drop out +); +void draw_meanlines( //draw a block + TO_BLOCK *block, //block to draw + float gradient, //gradients of lines + INT32 left, //edge of block + COLOUR colour, //colour to draw in + FCOORD rotation //rotation for line + ); +void plot_word_decisions( //draw words + WINDOW win, //window tro draw in + INT16 pitch, //of block + TO_ROW *row //row to draw + ); +void plot_fp_cells( //draw words + WINDOW win, //window tro draw in + COLOUR colour, //colour of lines + BLOBNBOX_IT *blob_it, //blobs + INT16 pitch, //of block + INT16 blob_count, //no of real blobs + STATS *projection, //vertical + INT16 projection_left, //edges //scale factor + INT16 projection_right, + float projection_scale); +void plot_fp_cells2( //draw words + WINDOW win, //window tro draw in + COLOUR colour, //colour of lines + TO_ROW *row, //for location + FPSEGPT_LIST *seg_list //segments to plot + ); +void plot_row_cells( //draw words + WINDOW win, //window tro draw in + COLOUR colour, //colour of lines + TO_ROW *row, //for location + float xshift, //amount of shift + ICOORDELT_LIST *cells //cells to draw + ); +#endif diff --git a/textord/edgblob.cpp b/textord/edgblob.cpp new file mode 100644 index 0000000000..7eabfa1fc2 --- /dev/null +++ b/textord/edgblob.cpp @@ -0,0 +1,407 @@ +/********************************************************************** + * File: edgblob.c (Formerly edgeloop.c) + * Description: Functions to clean up an outline before approximation. + * Author: Ray Smith + * Created: Tue Mar 26 16:56:25 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +//#include "dirtab.h" +#include "scanedg.h" +#include "drawedg.h" +#include "edgloop.h" +#include "edgblob.h" + +#define EXTERN + +EXTERN INT_VAR (edges_children_per_grandchild, 10, +"Importance ratio for chucking outlines"); +EXTERN INT_VAR (edges_children_count_limit, 45, "Max holes allowed in blob"); +EXTERN BOOL_VAR (edges_children_fix, FALSE, +"Remove boxy parents of char-like children"); +EXTERN INT_VAR (edges_min_nonhole, 12, +"Min pixels for potential char in box"); +EXTERN INT_VAR (edges_patharea_ratio, 40, +"Max lensq/area for acceptable child outline"); +EXTERN double_VAR (edges_childarea, 0.5, +"Max area fraction of child outline"); +EXTERN double_VAR (edges_boxarea, 0.8, +"Min area fraction of grandchild for box"); + +/********************************************************************** + * OL_BUCKETS::OL_BUCKETS + * + * Construct an array of buckets for associating outlines into blobs. + **********************************************************************/ + +OL_BUCKETS::OL_BUCKETS ( +////constructor +ICOORD bleft, //corners +ICOORD tright): bl (bleft), tr (tright) { + + bxdim = (tright.x () - bleft.x ()) / BUCKETSIZE + 1; + bydim = (tright.y () - bleft.y ()) / BUCKETSIZE + 1; + //make array + buckets = new C_OUTLINE_LIST[bxdim * bydim]; + index = 0; +} + + +/********************************************************************** + * OL_BUCKETS::operator( + * + * Return a pointer to a list of C_OUTLINEs corresponding to the + * given pixel coordinates. + **********************************************************************/ + +C_OUTLINE_LIST * +OL_BUCKETS::operator () ( //array access +INT16 x, //image coords +INT16 y) { + return &buckets[(y - bl.y ()) / BUCKETSIZE * bxdim + + (x - bl.x ()) / BUCKETSIZE]; +} + + +/********************************************************************** + * OL_BUCKETS::count_children + * + * Find number of descendants of this outline. + **********************************************************************/ + +INT32 OL_BUCKETS::count_children( //recursive count + C_OUTLINE *outline, //parent outline + INT32 max_count //max output + ) { + BOOL8 parent_box; //could it be boxy + INT16 xmin, xmax; //coord limits + INT16 ymin, ymax; + INT16 xindex, yindex; //current bucket + C_OUTLINE *child; //current child + INT32 child_count; //no of children + INT32 grandchild_count; //no of grandchildren + INT32 parent_area; //potential box + FLOAT32 max_parent_area; //potential box + INT32 child_area; //current child + INT32 child_length; //current child + BOX olbox; + C_OUTLINE_IT child_it; //search iterator + + olbox = outline->bounding_box (); + xmin = (olbox.left () - bl.x ()) / BUCKETSIZE; + xmax = (olbox.right () - bl.x ()) / BUCKETSIZE; + ymin = (olbox.bottom () - bl.y ()) / BUCKETSIZE; + ymax = (olbox.top () - bl.y ()) / BUCKETSIZE; + child_count = 0; + grandchild_count = 0; + parent_area = 0; + max_parent_area = 0; + parent_box = TRUE; + for (yindex = ymin; yindex <= ymax; yindex++) { + for (xindex = xmin; xindex <= xmax; xindex++) { + child_it.set_to_list (&buckets[yindex * bxdim + xindex]); + if (child_it.empty ()) + continue; + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + if (child != outline && *child < *outline) { + child_count++; + if (child_count <= max_count) + grandchild_count += count_children (child, + (max_count - + child_count) / + edges_children_per_grandchild) + * edges_children_per_grandchild; + if (child_count + grandchild_count > max_count) { + /* err.log(RESULT_OKAY,E_LOC,ERR_OCR, + ERR_SCROLLING,ERR_CONTINUE,ERR_DEBUG, + "Discarding parent with child count=%d, gc=%d", + child_count,grandchild_count);*/ + return child_count + grandchild_count; + } + if (parent_area == 0) { + parent_area = outline->outer_area (); + if (parent_area < 0) + parent_area = -parent_area; + max_parent_area = outline->bounding_box ().width () + * outline->bounding_box ().height () * edges_boxarea; + if (parent_area < max_parent_area) + parent_box = FALSE; + } + if (parent_box + && (!edges_children_fix + || child->bounding_box ().height () > + edges_min_nonhole) /**/) { + child_area = child->outer_area (); + if (child_area < 0) + child_area = -child_area; + if (edges_children_fix) { + if (parent_area - child_area < max_parent_area) { + parent_box = FALSE; + continue; + } + if (grandchild_count > 0) { + /* err.log(RESULT_OKAY,E_LOC,ERR_OCR, + ERR_SCROLLING,ERR_CONTINUE,ERR_DEBUG, + "Discarding parent of area %d, child area=%d, max%g with gc=%d", + parent_area,child_area,max_parent_area,grandchild_count);*/ + return max_count + 1; + } + child_length = child->pathlength (); + if (child_length * child_length > + child_area * edges_patharea_ratio) { + /*/ err.log(RESULT_OKAY,E_LOC,ERR_OCR, + ERR_SCROLLING,ERR_CONTINUE,ERR_DEBUG, + "Discarding parent of area %d, child area=%d, max%g with child length=%d", + parent_area,child_area,max_parent_area,child_length);*/ + return max_count + 1; + } + } + if (child_area < child->bounding_box ().width () + * child->bounding_box ().height () * + edges_childarea) { + /* err.log(RESULT_OKAY,E_LOC,ERR_OCR, + ERR_SCROLLING,ERR_CONTINUE,ERR_DEBUG, + "Discarding parent of area %d, child area=%d, max%g with child rect=%d", + parent_area,child_area,max_parent_area,child->bounding_box().width() + *child->bounding_box().height()); */ + return max_count + 1; + } + } + } + } + } + } + return child_count + grandchild_count; +} + + +/********************************************************************** + * OL_BUCKETS::extract_children + * + * Find number of descendants of this outline. + **********************************************************************/ + +void OL_BUCKETS::extract_children( //recursive count + C_OUTLINE *outline, //parent outline + C_OUTLINE_IT *it //destination iterator + ) { + INT16 xmin, xmax; //coord limits + INT16 ymin, ymax; + INT16 xindex, yindex; //current bucket + BOX olbox; + C_OUTLINE_IT child_it; //search iterator + + olbox = outline->bounding_box (); + xmin = (olbox.left () - bl.x ()) / BUCKETSIZE; + xmax = (olbox.right () - bl.x ()) / BUCKETSIZE; + ymin = (olbox.bottom () - bl.y ()) / BUCKETSIZE; + ymax = (olbox.top () - bl.y ()) / BUCKETSIZE; + for (yindex = ymin; yindex <= ymax; yindex++) { + for (xindex = xmin; xindex <= xmax; xindex++) { + child_it.set_to_list (&buckets[yindex * bxdim + xindex]); + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + if (*child_it.data () < *outline) { + it->add_after_then_move (child_it.extract ()); + } + } + } + } +} + + +/********************************************************************** + * extract_edges + * + * Run the edge detector over the block and return a list of blobs. + **********************************************************************/ + +void extract_edges( //find blobs +#ifndef GRAPHICS_DISABLED + WINDOW window, //window for output +#endif + IMAGE *image, //image to scan + IMAGE *t_image, //thresholded image + ICOORD page_tr, //corner of page + BLOCK *block //block to scan + ) { + ICOORD bleft; //block box + ICOORD tright; + C_OUTLINE_LIST outlines; //outlines in block + //iterator + C_OUTLINE_IT out_it = &outlines; + +#ifndef GRAPHICS_DISABLED + get_outlines (window, image, t_image, page_tr, (PDBLK *) block, &out_it); +#else + get_outlines (image, t_image, page_tr, (PDBLK *) block, &out_it); +#endif + //block box + block->bounding_box (bleft, tright); + //make blobs + outlines_to_blobs(block, bleft, tright, &outlines); + +} + + +/********************************************************************** + * outlines_to_blobs + * + * Gather together outlines into blobs using the usual bucket sort. + **********************************************************************/ + +void outlines_to_blobs( //find blobs + BLOCK *block, //block to scan + ICOORD bleft, //block box //outlines in block + ICOORD tright, + C_OUTLINE_LIST *outlines) { + //make buckets + OL_BUCKETS buckets(bleft, tright); + + fill_buckets(outlines, &buckets); + empty_buckets(block, &buckets); +} + + +/********************************************************************** + * fill_buckets + * + * Run the edge detector over the block and return a list of blobs. + **********************************************************************/ + +void fill_buckets( //find blobs + C_OUTLINE_LIST *outlines, //outlines in block + OL_BUCKETS *buckets //output buckets + ) { + BOX ol_box; //outline box + C_OUTLINE_IT out_it = outlines;//iterator + C_OUTLINE_IT bucket_it; //iterator in bucket + C_OUTLINE *outline; //current outline + + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + outline = out_it.extract (); //take off list + //get box + ol_box = outline->bounding_box (); + bucket_it.set_to_list ((*buckets) (ol_box.left (), ol_box.bottom ())); + bucket_it.add_to_end (outline); + } +} + + +/********************************************************************** + * empty_buckets + * + * Run the edge detector over the block and return a list of blobs. + **********************************************************************/ + +void empty_buckets( //find blobs + BLOCK *block, //block to scan + OL_BUCKETS *buckets //output buckets + ) { + BOOL8 good_blob; //healthy blob + C_OUTLINE_LIST outlines; //outlines in block + //iterator + C_OUTLINE_IT out_it = &outlines; + C_OUTLINE_IT bucket_it = buckets->start_scan (); + C_OUTLINE_IT parent_it; //parent outline + C_BLOB *blob; //new blob + C_BLOB_IT good_blobs = block->blob_list (); + C_BLOB_IT junk_blobs = block->reject_blobs (); + + while (!bucket_it.empty ()) { + out_it.set_to_list (&outlines); + do { + parent_it = bucket_it; //find outermost + do + bucket_it.forward (); + while (!bucket_it.at_first () + && !(*parent_it.data () < *bucket_it.data ())); + } + while (!bucket_it.at_first ()); + + //move to new list + out_it.add_after_then_move (parent_it.extract ()); + good_blob = capture_children (buckets, &junk_blobs, &out_it); + blob = new C_BLOB (&outlines); + if (good_blob) + good_blobs.add_after_then_move (blob); + else + junk_blobs.add_after_then_move (blob); + + bucket_it.set_to_list (buckets->scan_next ()); + } +} + + +/********************************************************************** + * capture_children + * + * Find all neighbouring outlines that are children of this outline + * and either move them to the output list or declare this outline + * illegal and return FALSE. + **********************************************************************/ + +BOOL8 capture_children( //find children + OL_BUCKETS *buckets, //bucket sort clanss + C_BLOB_IT *reject_it, //dead grandchildren + C_OUTLINE_IT *blob_it //output outlines + ) { + BOOL8 anydone; //anything canned + C_OUTLINE *outline; //master outline + C_OUTLINE *child; //child under test + C_OUTLINE_IT test_it; //for grandchildren + INT32 child_count; //no of children + C_BLOB *blob; //reject + C_OUTLINE_LIST r_list; //rejects + C_OUTLINE_IT r_it; //iterator + + outline = blob_it->data (); + child_count = buckets->count_children (outline, edges_children_count_limit); + if (child_count > edges_children_count_limit) + return FALSE; + if (child_count == 0) + return TRUE; + //get single level + buckets->extract_children (outline, blob_it); + if (child_count == 1) + return TRUE; + do { + anydone = FALSE; + blob_it->move_to_first (); + for (blob_it->mark_cycle_pt (); !blob_it->cycled_list (); + blob_it->forward ()) { + child = blob_it->data (); + if (child != outline) { + for (test_it = *blob_it, test_it.mark_cycle_pt (); + !test_it.cycled_list (); test_it.forward ()) { + if (test_it.data () != child && *test_it.data () < *child) { + r_it.set_to_list (&r_list); + r_it.add_after_then_move (test_it.extract ()); + //turn to blob + blob = new C_BLOB (&r_list); + reject_it->add_after_then_move (blob); + anydone = TRUE; + } + } + if (anydone) + break; //got to reatart + } + } + } + while (anydone); //got to restart + return TRUE; +} diff --git a/textord/edgblob.h b/textord/edgblob.h new file mode 100644 index 0000000000..ab073c23df --- /dev/null +++ b/textord/edgblob.h @@ -0,0 +1,100 @@ +/********************************************************************** + * File: edgblob.h (Formerly edgeloop.h) + * Description: Functions to clean up an outline before approximation. + * Author: Ray Smith + * Created: Tue Mar 26 16:56:25 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef EDGBLOB_H +#define EDGBLOB_H + +#include "grphics.h" +#include "varable.h" +#include "img.h" +#include "ocrblock.h" +#include "coutln.h" +#include "crakedge.h" +#include "notdll.h" + +#define BUCKETSIZE 16 + +class OL_BUCKETS +{ + public: + OL_BUCKETS( //constructor + ICOORD bleft, //corners + ICOORD tright); + + ~OL_BUCKETS () { //cleanup + delete[]buckets; + } + C_OUTLINE_LIST *operator () (//array access + INT16 x, //image coords + INT16 y); + //first non-empty bucket + C_OUTLINE_LIST *start_scan() { + for (index = 0; buckets[index].empty () && index < bxdim * bydim - 1; + index++); + return &buckets[index]; + } + //next non-empty bucket + C_OUTLINE_LIST *scan_next() { + for (; buckets[index].empty () && index < bxdim * bydim - 1; index++); + return &buckets[index]; + } + INT32 count_children( //recursive sum + C_OUTLINE *outline, //parent outline + INT32 max_count); //max output + void extract_children( //single level get + C_OUTLINE *outline, //parent outline + C_OUTLINE_IT *it); //destination iterator + + private: + C_OUTLINE_LIST * buckets; //array of buckets + INT16 bxdim; //size of array + INT16 bydim; + ICOORD bl; //corners + ICOORD tr; + INT32 index; //for extraction scan +}; + +void extract_edges( //find blobs +#ifndef GRAPHICS_DISABLED + WINDOW window, //window for output +#endif + IMAGE *image, //image to scan + IMAGE *t_image, //thresholded image + ICOORD page_tr, //corner of page + BLOCK *block //block to scan + ); +void outlines_to_blobs( //find blobs + BLOCK *block, //block to scan + ICOORD bleft, //block box //outlines in block + ICOORD tright, + C_OUTLINE_LIST *outlines); +void fill_buckets( //find blobs + C_OUTLINE_LIST *outlines, //outlines in block + OL_BUCKETS *buckets //output buckets + ); +void empty_buckets( //find blobs + BLOCK *block, //block to scan + OL_BUCKETS *buckets //output buckets + ); +BOOL8 capture_children( //find children + OL_BUCKETS *buckets, //bucket sort clanss + C_BLOB_IT *reject_it, //dead grandchildren + C_OUTLINE_IT *blob_it //output outlines + ); +#endif diff --git a/textord/edgloop.cpp b/textord/edgloop.cpp new file mode 100644 index 0000000000..96cce5b1f6 --- /dev/null +++ b/textord/edgloop.cpp @@ -0,0 +1,210 @@ +/********************************************************************** + * File: edgloop.c (Formerly edgeloop.c) + * Description: Functions to clean up an outline before approximation. + * Author: Ray Smith + * Created: Tue Mar 26 16:56:25 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "scanedg.h" +#include "drawedg.h" +#include "edgloop.h" + +#define MINEDGELENGTH 8 //min decent length + +#define EXTERN + +EXTERN double_VAR (edges_threshold_greyfraction, 0.07, +"Min edge diff for grad vector"); +EXTERN BOOL_VAR (edges_show_paths, FALSE, "Draw raw outlines"); +EXTERN BOOL_VAR (edges_show_needles, FALSE, "Draw edge needles"); +EXTERN INT_VAR (edges_maxedgelength, 16000, "Max steps in any outline"); + +#ifndef GRAPHICS_DISABLED +static WINDOW edge_win; //window +#endif +static C_OUTLINE_IT *outline_it; //iterator +static int short_edges; //no of short ones +static int long_edges; //no of long ones + +/********************************************************************** + * get_outlines + * + * Run the edge detector over the block and return a list of outlines. + **********************************************************************/ + +DLLSYM void get_outlines( //edge detect +#ifndef GRAPHICS_DISABLED + WINDOW window, //window for output +#endif + IMAGE *image, //image to scan + IMAGE *t_image, //thresholded image + ICOORD page_tr, //corner of page + PDBLK *block, //block to scan + C_OUTLINE_IT *out_it //output iterator + ) { +#ifndef GRAPHICS_DISABLED + edge_win = window; //set statics +#endif + outline_it = out_it; + block_edges(t_image, block, page_tr); + out_it->move_to_first (); +#ifndef GRAPHICS_DISABLED + if (window != NO_WINDOW) + overlap_picture_ops(TRUE); //update window +#endif +} + + +/********************************************************************** + * complete_edge + * + * Complete the edge by cleaning it up andapproximating it. + **********************************************************************/ + +void complete_edge( //clean and approximate + CRACKEDGE *start //start of loop + ) { + COLOUR colour; //colour to draw in + INT16 looplength; //steps in loop + ICOORD botleft; //bounding box + ICOORD topright; + C_OUTLINE *outline; //new outline + + //check length etc. + colour = check_path_legal (start); +#ifndef GRAPHICS_DISABLED + if (edges_show_paths) { + //in red + draw_raw_edge(edge_win, start, colour); + } +#endif + + if (colour == RED || colour == BLUE) { + looplength = loop_bounding_box (start, botleft, topright); + outline = new C_OUTLINE (start, botleft, topright, looplength); + //add to list + outline_it->add_after_then_move (outline); + } +} + + +/********************************************************************** + * check_path_legal + * + * Check that the outline is legal for length and for chaincode sum. + * The return value is RED for a normal black-inside outline, + * BLUE for a white-inside outline, MAGENTA if it is too short, + * YELLOW if it is too long, and GREEN if it is illegal. + * These colours are used to draw the raw outline. + **********************************************************************/ + +COLOUR check_path_legal( //certify outline + CRACKEDGE *start //start of loop + ) { + int lastchain; //last chain code + int chaindiff; //chain code diff + INT32 length; //length of loop + INT32 chainsum; //sum of chain diffs + CRACKEDGE *edgept; //current point + const ERRCODE ED_ILLEGAL_SUM = "Illegal sum of chain codes"; + + length = 0; + chainsum = 0; //sum of chain codes + edgept = start; + lastchain = edgept->prev->stepdir; //previous chain code + do { + length++; + if (edgept->stepdir != lastchain) { + //chain code difference + chaindiff = edgept->stepdir - lastchain; + if (chaindiff > 2) + chaindiff -= 4; + else if (chaindiff < -2) + chaindiff += 4; + chainsum += chaindiff; //sum differences + lastchain = edgept->stepdir; + } + edgept = edgept->next; + } + while (edgept != start && length < edges_maxedgelength); + + if (chainsum != 4 && chainsum != -4 + || edgept != start || length < MINEDGELENGTH) { + if (edgept != start) { + long_edges++; + return YELLOW; + } + else if (length < MINEDGELENGTH) { + short_edges++; + return MAGENTA; + } + else { + ED_ILLEGAL_SUM.error ("check_path_legal", LOG, "chainsum=%d", + chainsum); + return GREEN; + } + } + //colour on inside + return chainsum < 0 ? BLUE : RED; +} + +/********************************************************************** + * loop_bounding_box + * + * Find the bounding box of the edge loop. + **********************************************************************/ + +INT16 loop_bounding_box( //get bounding box + CRACKEDGE *&start, //edge loop + ICOORD &botleft, //bounding box + ICOORD &topright) { + INT16 length; //length of loop + INT16 leftmost; //on top row + CRACKEDGE *edgept; //current point + CRACKEDGE *realstart; //topleft start + + edgept = start; + realstart = start; + botleft = topright = ICOORD (edgept->pos.x (), edgept->pos.y ()); + leftmost = edgept->pos.x (); + length = 0; //coutn length + do { + edgept = edgept->next; + if (edgept->pos.x () < botleft.x ()) + //get bounding box + botleft.set_x (edgept->pos.x ()); + else if (edgept->pos.x () > topright.x ()) + topright.set_x (edgept->pos.x ()); + if (edgept->pos.y () < botleft.y ()) + //get bounding box + botleft.set_y (edgept->pos.y ()); + else if (edgept->pos.y () > topright.y ()) { + realstart = edgept; + leftmost = edgept->pos.x (); + topright.set_y (edgept->pos.y ()); + } + else if (edgept->pos.y () == topright.y () + && edgept->pos.x () < leftmost) { + //leftmost on line + leftmost = edgept->pos.x (); + realstart = edgept; + } + length++; //count elements + } + while (edgept != start); + start = realstart; //shift it to topleft + return length; +} diff --git a/textord/edgloop.h b/textord/edgloop.h new file mode 100644 index 0000000000..2ae1729267 --- /dev/null +++ b/textord/edgloop.h @@ -0,0 +1,66 @@ +/********************************************************************** + * File: edgloop.h (Formerly edgeloop.h) + * Description: Functions to clean up an outline before approximation. + * Author: Ray Smith + * Created: Tue Mar 26 16:56:25 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef EDGLOOP_H +#define EDGLOOP_H + +#include "grphics.h" +#include "varable.h" +#include "img.h" +#include "pdblock.h" +#include "coutln.h" +#include "crakedge.h" + +#define BUCKETSIZE 16 + + +extern double_VAR_H (edges_threshold_greyfraction, 0.07, +"Min edge diff for grad vector"); +extern BOOL_VAR_H (edges_show_paths, FALSE, "Draw raw outlines"); +extern BOOL_VAR_H (edges_show_needles, FALSE, "Draw edge needles"); +extern INT_VAR_H (edges_children_per_grandchild, 10, +"Importance ratio for chucking outlines"); +extern INT_VAR_H (edges_children_count_limit, 45, +"Max holes allowed in blob"); +extern INT_VAR_H (edges_maxedgelength, 16000, "Max steps in any outline"); +extern double_VAR_H (edges_childarea, 0.5, +"Max area fraction of child outline"); +extern double_VAR_H (edges_boxarea, 0.8, +"Min area fraction of grandchild for box"); +DLLSYM void get_outlines( //edge detect +#ifndef GRAPHICS_DISABLED + WINDOW window, //window for output +#endif + IMAGE *image, //image to scan + IMAGE *t_image, //thresholded image + ICOORD page_tr, //corner of page + PDBLK *block, //block to scan + C_OUTLINE_IT *out_it //output iterator + ); +void complete_edge( //clean and approximate + CRACKEDGE *start //start of loop + ); +COLOUR check_path_legal( //certify outline + CRACKEDGE *start //start of loop + ); +INT16 loop_bounding_box( //get bounding box + CRACKEDGE *&start, //edge loop + ICOORD &botleft, //bounding box + ICOORD &topright); +#endif diff --git a/textord/fpchop.cpp b/textord/fpchop.cpp new file mode 100644 index 0000000000..1c4bba5072 --- /dev/null +++ b/textord/fpchop.cpp @@ -0,0 +1,1641 @@ +/********************************************************************** + * File: fpchop.cpp (Formerly fp_chop.c) + * Description: Code to chop fixed pitch text into character cells. + * Author: Ray Smith + * Created: Thu Sep 16 11:14:15 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include "stderr.h" +#include "blobbox.h" +#include "lmedsq.h" +#include "statistc.h" +#include "drawtord.h" +#include "tovars.h" +#include "topitch.h" +#include "fpchop.h" +#include "notdll.h" + +#define EXTERN + +EXTERN INT_VAR (textord_fp_chop_error, 2, +"Max allowed bending of chop cells"); +EXTERN double_VAR (textord_fp_chop_snap, 0.5, +"Max distance of chop pt from vertex"); + +ELISTIZE (OUTLINE_FRAG) ELISTIZE (C_OUTLINE_FRAG) +//#undef ASSERT_HOST +//#define ASSERT_HOST(x) if (!(x)) AfxMessageBox(#x); +/********************************************************************** + * fixed_pitch_words + * + * Make a ROW from a fixed pitch TO_ROW. + **********************************************************************/ +ROW *fixed_pitch_words( //find lines + TO_ROW *row, //row to do + FCOORD rotation //for drawing + ) { + BOOL8 bol; //start of line + UINT8 blanks; //in front of word + UINT8 new_blanks; //blanks in empty cell + INT16 chop_coord; //chop boundary + INT16 prev_chop_coord; //start of cell + INT16 rep_left; //left edge of rep word + ROW *real_row; //output row + OUTLINE_LIST left_outlines; //in current blob + OUTLINE_LIST right_outlines; //for next blob + C_OUTLINE_LIST left_coutlines; + C_OUTLINE_LIST right_coutlines; + PBLOB_LIST blobs; //blobs in word + C_BLOB_LIST cblobs; + PBLOB_IT blob_it = &blobs; //iterator + C_BLOB_IT cblob_it = &cblobs; + WERD_LIST words; + WERD_IT word_it = &words; //new words + //repeated blobs + WERD_IT rep_it = &row->rep_words; + WERD *word; //new word + INT32 xstarts[2]; //row ends + double coeffs[3]; //quadratic + INT32 prev_x; //end of prev blob + //iterator + BLOBNBOX_IT box_it = row->blob_list (); + //boundaries + ICOORDELT_IT cell_it = &row->char_cells; + +#ifndef GRAPHICS_DISABLED + if (textord_show_page_cuts && to_win != NO_WINDOW) { + plot_row_cells (to_win, RED, row, 0, &row->char_cells); + } +#endif + + prev_x = -MAX_INT16; + bol = TRUE; + blanks = 0; + if (rep_it.empty ()) + rep_left = MAX_INT16; + else + rep_left = rep_it.data ()->bounding_box ().left (); + if (box_it.empty ()) + return NULL; //empty row + xstarts[0] = box_it.data ()->bounding_box ().left (); + if (rep_left < xstarts[0]) { + xstarts[0] = rep_left; + } + if (cell_it.empty () || row->char_cells.singleton ()) { + tprintf ("Row without enough char cells!\n"); + tprintf ("Leftmost blob is at (%d,%d)\n", + box_it.data ()->bounding_box ().left (), + box_it.data ()->bounding_box ().bottom ()); + return NULL; + } + ASSERT_HOST (!cell_it.empty () && !row->char_cells.singleton ()); + prev_chop_coord = cell_it.data ()->x (); + word = NULL; + while (rep_left < cell_it.data ()->x ()) { + word = add_repeated_word (&rep_it, rep_left, prev_chop_coord, + blanks, row->fixed_pitch, &word_it); + } + cell_it.mark_cycle_pt (); + if (prev_chop_coord >= cell_it.data ()->x ()) + cell_it.forward (); + for (; !cell_it.cycled_list (); cell_it.forward ()) { + chop_coord = cell_it.data ()->x (); + while (!box_it.empty () + && box_it.data ()->bounding_box ().left () <= chop_coord) { + if (box_it.data ()->bounding_box ().right () > prev_x) + prev_x = box_it.data ()->bounding_box ().right (); + split_to_blob (box_it.extract (), chop_coord, + textord_fp_chop_error + 0.5f, + &left_outlines, &left_coutlines, + &right_outlines, &right_coutlines); + box_it.forward (); + while (!box_it.empty () + && box_it.data ()->blob () == NULL + && box_it.data ()->cblob () == NULL) { + delete box_it.extract (); + box_it.forward (); + } + } + if ((!right_outlines.empty () || !right_coutlines.empty ()) + && left_outlines.empty () && left_coutlines.empty ()) + split_to_blob (NULL, chop_coord, + textord_fp_chop_error + 0.5f, + &left_outlines, &left_coutlines, + &right_outlines, &right_coutlines); + if (!left_outlines.empty ()) + blob_it.add_after_then_move (new PBLOB (&left_outlines)); + else if (!left_coutlines.empty ()) + cblob_it.add_after_then_move (new C_BLOB (&left_coutlines)); + else { + if (rep_left < chop_coord) { + if (rep_left > prev_chop_coord) + new_blanks = (UINT8) floor ((rep_left - prev_chop_coord) + / row->fixed_pitch + 0.5); + else + new_blanks = 0; + } + else { + if (chop_coord > prev_chop_coord) + new_blanks = (UINT8) floor ((chop_coord - prev_chop_coord) + / row->fixed_pitch + 0.5); + else + new_blanks = 0; + } + if (!blob_it.empty () || !cblob_it.empty ()) { + if (blanks < 1 && word != NULL && !word->flag (W_REP_CHAR)) + blanks = 1; + if (!blob_it.empty ()) { + //make real word + word = new WERD (&blobs, blanks, NULL); + blob_it.set_to_list (&blobs); + } + else { + word = new WERD (&cblobs, blanks, NULL); + cblob_it.set_to_list (&cblobs); + } + word->set_flag (W_DONT_CHOP, TRUE); + word_it.add_after_then_move (word); + if (bol) { + word->set_flag (W_BOL, TRUE); + bol = FALSE; + } + blanks = new_blanks; + } + else + blanks += new_blanks; + while (rep_left < chop_coord) { + word = add_repeated_word (&rep_it, rep_left, prev_chop_coord, + blanks, row->fixed_pitch, &word_it); + } + } + if (prev_chop_coord < chop_coord) + prev_chop_coord = chop_coord; + } + if (!blob_it.empty () || !cblob_it.empty ()) { + if (!blob_it.empty ()) + //last word on line + word = new WERD (&blobs, blanks, NULL); + else + word = new WERD (&cblobs, blanks, NULL); + word->set_flag (W_DONT_CHOP, TRUE); + word_it.add_after_then_move (word); + if (bol) + word->set_flag (W_BOL, TRUE); + } + ASSERT_HOST (word != NULL); + while (!rep_it.empty ()) { + add_repeated_word (&rep_it, rep_left, prev_chop_coord, + blanks, row->fixed_pitch, &word_it); + } + //at end of line + word_it.data ()->set_flag (W_EOL, TRUE); + if (prev_chop_coord > prev_x) + prev_x = prev_chop_coord; + xstarts[1] = prev_x + 1; + coeffs[0] = 0; + coeffs[1] = row->line_m (); + coeffs[2] = row->line_c (); + real_row = new ROW (row, (INT16) row->kern_size, (INT16) row->space_size); + word_it.set_to_list (real_row->word_list ()); + //put words in row + word_it.add_list_after (&words); + real_row->recalc_bounding_box (); + return real_row; +} + + +/********************************************************************** + * add_repeated_word + * + * Add repeated word into the row at the given point. + **********************************************************************/ + +WERD *add_repeated_word( //move repeated word + WERD_IT *rep_it, //repeated words + INT16 &rep_left, //left edge of word + INT16 &prev_chop_coord, //previous word end + UINT8 &blanks, //no of blanks + float pitch, //char cell size + WERD_IT *word_it //list of words + ) { + WERD *word; //word to move + INT16 new_blanks; //extra blanks + + if (rep_left > prev_chop_coord) { + new_blanks = (UINT8) floor ((rep_left - prev_chop_coord) / pitch + 0.5); + blanks += new_blanks; + } + word = rep_it->extract (); + prev_chop_coord = word->bounding_box ().right (); + word_it->add_after_then_move (word); + word->set_blanks (blanks); + rep_it->forward (); + if (rep_it->empty ()) + rep_left = MAX_INT16; + else + rep_left = rep_it->data ()->bounding_box ().left (); + blanks = 0; + return word; +} + + +/********************************************************************** + * split_to_blob + * + * Split a BLOBNBOX across a vertical chop line and put the pieces + * into a left outline list and a right outline list. + **********************************************************************/ + +void split_to_blob( //split the blob + BLOBNBOX *blob, //blob to split + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + OUTLINE_LIST *left_outlines, //left half of chop + C_OUTLINE_LIST *left_coutlines, //for cblobs + OUTLINE_LIST *right_outlines, //right half of chop + C_OUTLINE_LIST *right_coutlines) { + PBLOB *real_blob; //blob to chop + C_BLOB *real_cblob; //cblob to chop + + if (blob != NULL) { + real_blob = blob->blob (); + real_cblob = blob->cblob (); + } + else { + real_blob = NULL; + real_cblob = NULL; + } + if (!right_outlines->empty () || real_blob != NULL) + fixed_chop_blob(real_blob, + chop_coord, + pitch_error, + left_outlines, + right_outlines); + else if (!right_coutlines->empty () || real_cblob != NULL) + fixed_chop_cblob(real_cblob, + chop_coord, + pitch_error, + left_coutlines, + right_coutlines); + if (blob != NULL) + delete blob; //free it +} + + +/********************************************************************** + * fixed_chop_blob + * + * Chop the given blob (if any) and the existing right outlines to + * produce a list of outlines left of the chop point and more to the right. + **********************************************************************/ + +void fixed_chop_blob( //split the blob + PBLOB *blob, //blob to split + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + OUTLINE_LIST *left_outlines, //left half of chop + OUTLINE_LIST *right_outlines //right half of chop + ) { + OUTLINE *old_right; //already there + OUTLINE_LIST new_outlines; //new right ones + //ouput iterator + OUTLINE_IT left_it = left_outlines; + //in/out iterator + OUTLINE_IT right_it = right_outlines; + OUTLINE_IT new_it = &new_outlines; + OUTLINE_IT blob_it; //outlines in blob + + if (!right_it.empty ()) { + while (!right_it.empty ()) { + old_right = right_it.extract (); + right_it.forward (); + fixed_split_outline(old_right, + chop_coord, + pitch_error, + &left_it, + &new_it); + } + right_it.add_list_before (&new_outlines); + } + if (blob != NULL) { + blob_it.set_to_list (blob->out_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) + fixed_split_outline (blob_it.extract (), chop_coord, pitch_error, + &left_it, &right_it); + delete blob; + } +} + + +/********************************************************************** + * fixed_split_outline + * + * Chop the given outline (if necessary) placing the fragments which + * fall either side of the chop line into the appropriate list. + **********************************************************************/ + +void fixed_split_outline( //chop the outline + OUTLINE *srcline, //source outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + OUTLINE_IT *left_it, //left half of chop + OUTLINE_IT *right_it //right half of chop + ) { + OUTLINE *child; //child outline + BOX srcbox; //box of outline + OUTLINE_LIST left_ch; //left children + OUTLINE_LIST right_ch; //right children + OUTLINE_FRAG_LIST left_frags; //chopped fragments + OUTLINE_FRAG_LIST right_frags;; + OUTLINE_IT left_ch_it = &left_ch; + //for whole children + OUTLINE_IT right_ch_it = &right_ch; + //for holes + OUTLINE_IT child_it = srcline->child (); + + srcbox = srcline->bounding_box (); + //left of line + if (srcbox.left () + srcbox.right () <= chop_coord * 2 + //and not far over + && srcbox.right () < chop_coord + pitch_error) + //stick whole in left + left_it->add_after_then_move (srcline); + else if (srcbox.left () + srcbox.right () > chop_coord * 2 + && srcbox.left () > chop_coord - pitch_error) + //stick whole in right + right_it->add_before_stay_put (srcline); + else { + //needs real chopping + if (fixed_chop_outline (srcline, chop_coord, pitch_error, + &left_frags, &right_frags)) { + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.extract (); + srcbox = child->bounding_box (); + if (srcbox.right () < chop_coord) + left_ch_it.add_after_then_move (child); + else if (srcbox.left () > chop_coord) + right_ch_it.add_after_then_move (child); + else { + if (fixed_chop_outline (child, chop_coord, pitch_error, + &left_frags, &right_frags)) + delete child; + else { + if (srcbox.left () + srcbox.right () <= chop_coord * 2) + left_ch_it.add_after_then_move (child); + else + right_ch_it.add_after_then_move (child); + } + } + } + close_chopped_fragments(&left_frags, &left_ch, left_it); + close_chopped_fragments(&right_frags, &right_ch, right_it); + ASSERT_HOST (left_ch.empty () && right_ch.empty ()); + //no children left + delete srcline; //smashed up + } + else { + if (srcbox.left () + srcbox.right () <= chop_coord * 2) + //stick whole in left + left_it->add_after_then_move (srcline); + else + right_it->add_before_stay_put (srcline); + } + } +} + + +/********************************************************************** + * fixed_chop_outline + * + * Chop the given outline (if necessary) placing the fragments which + * fall either side of the chop line into the appropriate list. + * If the outline lies too heavily to one side to chop, FALSE is returned. + **********************************************************************/ + +BOOL8 fixed_chop_outline( //chop the outline + OUTLINE *srcline, //source outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + OUTLINE_FRAG_LIST *left_frags, //left half of chop + OUTLINE_FRAG_LIST *right_frags //right half of chop + ) { + BOOL8 not_first; //fragment + BOOL8 test_valid; //test pt valid + float left_edge; //of outline + FCOORD chop_pos; //coords of chop + float chop_starty; //test chop pt + POLYPT *startpt; //in first fragment + //general iterator + POLYPT_IT poly_it = srcline->polypts (); + POLYPT_IT head_it; //head of fragment + POLYPT_IT tail_it; //tail of fragment + POLYPT_IT test_tail; //possible chop pt + + left_edge = poly_it.data ()->pos.x (); + tail_it = poly_it; + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) { + if (poly_it.data ()->pos.x () < left_edge) { + left_edge = poly_it.data ()->pos.x (); + tail_it = poly_it; //find leftmost pt + } + } + if (left_edge >= chop_coord - pitch_error) + return FALSE; //not worth it + + startpt = tail_it.data (); + not_first = FALSE; + head_it = tail_it; + chop_starty = tail_it.data ()->pos.y (); + do { + test_valid = FALSE; + do { + tail_it.forward (); + if (test_valid + && tail_it.data ()->pos.x () >= chop_coord + && tail_it.data ()->pos.x () + tail_it.data ()->vec.x () <= + chop_coord) { + chop_pos = find_chop_coords (&tail_it, chop_coord); + if (chop_pos.y () >= chop_starty) + test_valid = FALSE; + else { + tail_it = test_tail; + break; //must chop there + } + } + if (tail_it.data ()->pos.x () <= chop_coord + && tail_it.data ()->pos.x () + tail_it.data ()->vec.x () >= + chop_coord) { + chop_pos = find_chop_coords (&tail_it, chop_coord); + chop_starty = chop_pos.y (); + test_tail = tail_it; //save possible chop pt + test_valid = TRUE; + if (tail_it.data ()->vec.x () == 0 + && tail_it.data ()->vec.y () < 0) + break; //must chop here + } + } + while (tail_it.data () != startpt + && tail_it.data ()->pos.x () < chop_coord + pitch_error); + //back to start + if (tail_it.data () == startpt) { + if (not_first) + break; + else + return FALSE; //doesn't cross line + } + while (tail_it.data ()->pos.x () > chop_coord) + tail_it.backward (); + if (head_it.data () == tail_it.data ()) + insert_extra_pt(&tail_it); + insert_chop_pt(&tail_it, chop_coord); + if (not_first) { + save_chop_fragment(&head_it, &tail_it, left_frags); + } + else { + tail_it.forward (); + head_it = tail_it; + } + test_valid = FALSE; + do { + tail_it.forward (); + if (test_valid + && tail_it.data ()->pos.x () <= chop_coord + && tail_it.data ()->pos.x () + tail_it.data ()->vec.x () >= + chop_coord) { + chop_pos = find_chop_coords (&tail_it, chop_coord); + if (chop_pos.y () <= chop_starty) + test_valid = FALSE; + else { + tail_it = test_tail; + break; //must chop there + } + } + if (tail_it.data ()->pos.x () >= chop_coord + && tail_it.data ()->pos.x () + tail_it.data ()->vec.x () <= + chop_coord) { + chop_pos = find_chop_coords (&tail_it, chop_coord); + chop_starty = chop_pos.y (); + test_tail = tail_it; + test_valid = TRUE; //save possible chop pt + if (tail_it.data ()->vec.x () == 0 + && tail_it.data ()->vec.y () > 0) + break; //must chop here + } + } + while (tail_it.data () != startpt + && tail_it.data ()->pos.x () > chop_coord - pitch_error); + while (tail_it.data ()->pos.x () < chop_coord) + tail_it.backward (); + if (head_it.data () == tail_it.data ()) + insert_extra_pt(&tail_it); + insert_chop_pt(&tail_it, chop_coord); + save_chop_fragment(&head_it, &tail_it, right_frags); + not_first = TRUE; + } + while (tail_it.data () != startpt); + startpt = head_it.data_relative (-1); + while (tail_it.data () != startpt) + tail_it.forward (); + save_chop_fragment(&head_it, &tail_it, left_frags); + return TRUE; //did some chopping +} + + +/********************************************************************** + * save_chop_fragment + * + * Store the given fragment in the given fragment list. + **********************************************************************/ + +void save_chop_fragment( //chop the outline + POLYPT_IT *head_it, //head of fragment + POLYPT_IT *tail_it, //tail of fragment + OUTLINE_FRAG_LIST *frags //fragment list + ) { + OUTLINE_FRAG *head; //head of fragment + OUTLINE_FRAG *tail; //tail of fragment + float tail_y; //ycoord of tail + + tail_y = tail_it->data ()->pos.y (); + head = new OUTLINE_FRAG (head_it, tail_it); + tail = new OUTLINE_FRAG (head, tail_y); + head->other_end = tail; + add_frag_to_list(head, frags); + add_frag_to_list(tail, frags); + head_it->forward (); + tail_it->forward (); +} + + +/********************************************************************** + * OUTLINE_FRAG::OUTLINE_FRAG + * + * Constructors for OUTLINE_FRAG. + **********************************************************************/ + +OUTLINE_FRAG::OUTLINE_FRAG( //record fragment + POLYPT_IT *head_it, //head of fragment + POLYPT_IT *tail_it //tail of fragment + ) { + ycoord = head_it->data ()->pos.y (); + other_end = NULL; + polypts.assign_to_sublist (head_it, tail_it); +} + + +OUTLINE_FRAG::OUTLINE_FRAG( //record fragment + OUTLINE_FRAG *head, //other end + float tail_y) { + ycoord = tail_y; + other_end = head; +} + + +/********************************************************************** + * add_frag_to_list + * + * Insert the fragment in the list at the appropriate place to keep + * them in ascending ycoord order. + **********************************************************************/ + +void add_frag_to_list( //ordered add + OUTLINE_FRAG *frag, //fragment to add + OUTLINE_FRAG_LIST *frags //fragment list + ) { + //output list + OUTLINE_FRAG_IT frag_it = frags; + + if (!frags->empty ()) { + for (frag_it.mark_cycle_pt (); !frag_it.cycled_list (); + frag_it.forward ()) { + if (frag_it.data ()->ycoord >= frag->ycoord) { + frag_it.add_before_then_move (frag); + return; + } + } + } + frag_it.add_to_end (frag); +} + + +/********************************************************************** + * insert_chop_pt + * + * Decide whether or not to use the actual point as chop coord. + * Insert either a duplicate of the current point or 2 copies + * of the new chop point. Position the iterator at the first. + **********************************************************************/ + +void insert_chop_pt( //make chop + POLYPT_IT *it, //iterator + INT16 chop_coord //required chop pt + ) { + POLYPT *prev_pt; //point befor chop + POLYPT *chop_pt; //new vertex + FCOORD chop_pos; //coords of chop + FCOORD chop_vec; //vector to next + + prev_pt = it->data (); + if (prev_pt->pos.x () + textord_fp_chop_snap >= chop_coord + && prev_pt->pos.x () - textord_fp_chop_snap <= chop_coord) { + chop_pt = new POLYPT (prev_pt->pos, prev_pt->vec); + } + else { + chop_pos = FCOORD (chop_coord, prev_pt->pos.y () + + prev_pt->vec.y () * (chop_coord - + prev_pt->pos.x ()) / + prev_pt->vec.x ()); + chop_vec = it->data_relative (1)->pos - chop_pos; + chop_pt = new POLYPT (chop_pos, chop_vec); + it->add_after_then_move (chop_pt); + chop_pt = new POLYPT (chop_pos, chop_vec); + } + it->add_after_stay_put (chop_pt); +} + + +/********************************************************************** + * find_chop_coords + * + * Decide whether or not to use the actual point as chop coord. + * Return the coords of the chop point. + **********************************************************************/ + +FCOORD find_chop_coords( //make chop + POLYPT_IT *it, //iterator + INT16 chop_coord //required chop pt + ) { + POLYPT *prev_pt; //point befor chop + FCOORD chop_pos; //coords of chop + + prev_pt = it->data (); + if (prev_pt->pos.x () + textord_fp_chop_snap >= chop_coord + && prev_pt->pos.x () - textord_fp_chop_snap <= chop_coord) { + chop_pos = prev_pt->pos; + } + else { + chop_pos = FCOORD (chop_coord, prev_pt->pos.y () + + prev_pt->vec.y () * (chop_coord - + prev_pt->pos.x ()) / + prev_pt->vec.x ()); + } + return chop_pos; +} + + +/********************************************************************** + * insert_extra_pt + * + * Add an extra pt to prevent single point fragments being made. + **********************************************************************/ + +void insert_extra_pt( //make extra + POLYPT_IT *it //iterator + ) { + POLYPT *prev_pt; //point befor chop + POLYPT *chop_pt; //new vertex + FCOORD chop_pos; //coords of chop + FCOORD chop_vec; //vector to next + + prev_pt = it->data (); + if (it->data_relative (1)->pos.y () > it->data_relative (-1)->pos.y ()) { + chop_pos = prev_pt->pos + FCOORD (0.0f, + static_cast(textord_fp_chop_snap)); + } + else { + chop_pos = prev_pt->pos - FCOORD (0.0f, + static_cast(textord_fp_chop_snap)); + } + chop_vec = it->data_relative (1)->pos - chop_pos; + prev_pt->vec = chop_pos - prev_pt->pos; + chop_pt = new POLYPT (chop_pos, chop_vec); + it->add_after_then_move (chop_pt); +} + + +/********************************************************************** + * close_chopped_fragments + * + * Clear the given list of fragments joining them up into outlines. + * Each outline made soaks up any of the child outlines which it encloses. + **********************************************************************/ + +void close_chopped_fragments( //chop the outline + OUTLINE_FRAG_LIST *frags, //list to clear + OUTLINE_LIST *children, //potential children + OUTLINE_IT *dest_it //output list + ) { + //iterator + OUTLINE_FRAG_IT frag_it = frags; + OUTLINE_FRAG *bottom_frag; //bottom of cut + OUTLINE_FRAG *top_frag; //top of cut + OUTLINE *outline; //new outline + OUTLINE *child; //current child + OUTLINE_IT child_it = children; + OUTLINE_IT olchild_it; //children of outline + POLYPT_IT poly_it; //iterator for constr + + while (!frag_it.empty ()) { + frag_it.move_to_first (); + //get bottom one + bottom_frag = frag_it.extract (); + frag_it.forward (); + //and one above it + top_frag = frag_it.extract (); + while (top_frag->other_end != bottom_frag) { + do { + frag_it.forward (); + } + //find other end + while (frag_it.data () != top_frag->other_end); + join_chopped_fragments(bottom_frag, top_frag); + delete top_frag; + delete frag_it.extract (); //remove middle section + frag_it.forward (); + top_frag = frag_it.extract (); + } + join_chopped_fragments(bottom_frag, top_frag); + if (bottom_frag->polypts.empty ()) + poly_it.set_to_list (&top_frag->polypts); + else + poly_it.set_to_list (&bottom_frag->polypts); + outline = new OUTLINE (&poly_it); + olchild_it.set_to_list (outline->child ()); + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + if (*child < *outline) + olchild_it.add_to_end (child_it.extract ()); + } + dest_it->add_after_then_move (outline); + } + while (!child_it.empty ()) { + dest_it->add_after_then_move (child_it.extract ()); + child_it.forward (); + } +} + + +/********************************************************************** + * join_chopped_fragments + * + * Join the two lists of POLYPTs such that the first OUTLINE_FRAG + * operand keeps responsibility for the fragment. + **********************************************************************/ + +void join_chopped_fragments( //join pieces + OUTLINE_FRAG *bottom, //bottom of cut + OUTLINE_FRAG *top //top of cut + ) { + POLYPT_IT master_it; //dest list + POLYPT_IT slave_it; //src list + POLYPT *cutpt; //vectors to change + POLYPT *nextpt; //other end of cut + + if (bottom->polypts.empty ()) { + master_it.set_to_list (&bottom->other_end->polypts); + cutpt = master_it.data_relative (-1); + ASSERT_HOST (!top->polypts.empty ()); + slave_it.set_to_list (&top->polypts); + nextpt = slave_it.data (); + if (bottom->other_end != top) { + master_it.move_to_last (); + master_it.add_list_after (&top->polypts); + } + } + else { + master_it.set_to_list (&bottom->polypts); + ASSERT_HOST (top->polypts.empty ()); + slave_it.set_to_list (&top->other_end->polypts); + cutpt = slave_it.data_relative (-1); + nextpt = master_it.data (); + if (bottom->other_end != top) + master_it.add_list_before (&top->other_end->polypts); + } + cutpt->vec = nextpt->pos - cutpt->pos; +} + + +/********************************************************************** + * fixed_chop_cblob + * + * Chop the given cblob (if any) and the existing right outlines to + * produce a list of outlines left of the chop point and more to the right. + **********************************************************************/ + +void fixed_chop_cblob( //split the blob + C_BLOB *blob, //blob to split + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + C_OUTLINE_LIST *left_outlines, //left half of chop + C_OUTLINE_LIST *right_outlines //right half of chop + ) { + C_OUTLINE *old_right; //already there + C_OUTLINE_LIST new_outlines; //new right ones + //ouput iterator + C_OUTLINE_IT left_it = left_outlines; + //in/out iterator + C_OUTLINE_IT right_it = right_outlines; + C_OUTLINE_IT new_it = &new_outlines; + C_OUTLINE_IT blob_it; //outlines in blob + + if (!right_it.empty ()) { + while (!right_it.empty ()) { + old_right = right_it.extract (); + right_it.forward (); + fixed_split_coutline(old_right, + chop_coord, + pitch_error, + &left_it, + &new_it); + } + right_it.add_list_before (&new_outlines); + } + if (blob != NULL) { + blob_it.set_to_list (blob->out_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) + fixed_split_coutline (blob_it.extract (), chop_coord, pitch_error, + &left_it, &right_it); + delete blob; + } +} + + +/********************************************************************** + * fixed_split_outline + * + * Chop the given outline (if necessary) placing the fragments which + * fall either side of the chop line into the appropriate list. + **********************************************************************/ + +void fixed_split_coutline( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + C_OUTLINE_IT *left_it, //left half of chop + C_OUTLINE_IT *right_it //right half of chop + ) { + C_OUTLINE *child; //child outline + BOX srcbox; //box of outline + C_OUTLINE_LIST left_ch; //left children + C_OUTLINE_LIST right_ch; //right children + C_OUTLINE_FRAG_LIST left_frags;//chopped fragments + C_OUTLINE_FRAG_LIST right_frags;; + C_OUTLINE_IT left_ch_it = &left_ch; + //for whole children + C_OUTLINE_IT right_ch_it = &right_ch; + //for holes + C_OUTLINE_IT child_it = srcline->child (); + + srcbox = srcline->bounding_box (); + //left of line + if (srcbox.left () + srcbox.right () <= chop_coord * 2 + //and not far over + && srcbox.right () < chop_coord + pitch_error) + //stick whole in left + left_it->add_after_then_move (srcline); + else if (srcbox.left () + srcbox.right () > chop_coord * 2 + && srcbox.left () > chop_coord - pitch_error) + //stick whole in right + right_it->add_before_stay_put (srcline); + else { + //needs real chopping + if (fixed_chop_coutline (srcline, chop_coord, pitch_error, + &left_frags, &right_frags)) { + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.extract (); + srcbox = child->bounding_box (); + if (srcbox.right () < chop_coord) + left_ch_it.add_after_then_move (child); + else if (srcbox.left () > chop_coord) + right_ch_it.add_after_then_move (child); + else { + if (fixed_chop_coutline (child, chop_coord, pitch_error, + &left_frags, &right_frags)) + delete child; + else { + if (srcbox.left () + srcbox.right () <= chop_coord * 2) + left_ch_it.add_after_then_move (child); + else + right_ch_it.add_after_then_move (child); + } + } + } + close_chopped_cfragments(&left_frags, &left_ch, pitch_error, left_it); + close_chopped_cfragments(&right_frags, &right_ch, pitch_error, right_it); + ASSERT_HOST (left_ch.empty () && right_ch.empty ()); + //no children left + delete srcline; //smashed up + } + else { + if (srcbox.left () + srcbox.right () <= chop_coord * 2) + //stick whole in left + left_it->add_after_then_move (srcline); + else + right_it->add_before_stay_put (srcline); + } + } +} + + +/********************************************************************** + * fixed_chop_coutline + * + * Chop the given coutline (if necessary) placing the fragments which + * fall either side of the chop line into the appropriate list. + * If the coutline lies too heavily to one side to chop, FALSE is returned. + **********************************************************************/ + +BOOL8 fixed_chop_coutline( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + C_OUTLINE_FRAG_LIST *left_frags, //left half of chop + C_OUTLINE_FRAG_LIST *right_frags //right half of chop + ) { + BOOL8 first_frag; //fragment + BOOL8 anticlock; //direction of loop + INT16 left_edge; //of outline + INT16 startindex; //in first fragment + INT32 length; //of outline + INT16 stepindex; //into outline + INT16 head_index; //start of fragment + ICOORD head_pos; //start of fragment + INT16 tail_index; //end of fragment + ICOORD tail_pos; //end of fragment + ICOORD pos; //current point + INT16 first_index = 0; //first tail + ICOORD first_pos; //first tail + + length = srcline->pathlength (); + pos = srcline->start_pos (); + anticlock = srcline->turn_direction () > 0; + left_edge = pos.x (); + tail_index = 0; + tail_pos = pos; + for (stepindex = 0; stepindex < length; stepindex++) { + if (pos.x () < left_edge) { + left_edge = pos.x (); + tail_index = stepindex; + tail_pos = pos; + } + pos += srcline->step (stepindex); + } + if (left_edge >= chop_coord - pitch_error) + return FALSE; //not worth it + + startindex = tail_index; + first_frag = TRUE; + head_index = tail_index; + head_pos = tail_pos; + do { + do { + tail_pos += srcline->step (tail_index); + tail_index++; + if (tail_index == length) + tail_index = 0; + } + while (tail_pos.x () != chop_coord && tail_index != startindex); + if (tail_index == startindex) { + if (first_frag) + return FALSE; //doesn't cross line + else + break; + } + //#ifdef __UNIX__ + ASSERT_HOST (head_index != tail_index); + //#endif + if (!first_frag) { + save_chop_cfragment(head_index, + head_pos, + tail_index, + tail_pos, + srcline, + left_frags); + } + else { + first_index = tail_index; + first_pos = tail_pos; + first_frag = FALSE; + } + while (srcline->step (tail_index).x () == 0) { + tail_pos += srcline->step (tail_index); + tail_index++; + if (tail_index == length) + tail_index = 0; + } + head_index = tail_index; + head_pos = tail_pos; + while (srcline->step (tail_index).x () > 0) { + do { + tail_pos += srcline->step (tail_index); + tail_index++; + if (tail_index == length) + tail_index = 0; + } + while (tail_pos.x () != chop_coord); + //#ifdef __UNIX__ + ASSERT_HOST (head_index != tail_index); + //#endif + save_chop_cfragment(head_index, + head_pos, + tail_index, + tail_pos, + srcline, + right_frags); + while (srcline->step (tail_index).x () == 0) { + tail_pos += srcline->step (tail_index); + tail_index++; + if (tail_index == length) + tail_index = 0; + } + head_index = tail_index; + head_pos = tail_pos; + } + } + while (tail_index != startindex); + save_chop_cfragment(head_index, + head_pos, + first_index, + first_pos, + srcline, + left_frags); + return TRUE; //did some chopping +} + + +/********************************************************************** + * next_anti_left_seg + * + * Search the outline for a suitable point at which it crosses the + * chop_coord from left to right. + **********************************************************************/ + +INT16 next_anti_left_seg( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 tail_index, //of tailpos + INT16 startindex, //end of search + INT32 length, //of outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + ICOORD *tail_pos //current position + ) { + BOOL8 test_valid; //test pt valid + INT16 chop_starty; //test chop pt + INT16 test_index; //possible chop pt + ICOORD test_pos; //possible chop pt + ICOORD prev_step; //in x to tail pos + + test_valid = FALSE; + chop_starty = -MAX_INT16; + test_index = tail_index; //stop warnings + do { + *tail_pos += srcline->step (tail_index); + prev_step = srcline->step (tail_index); + tail_index++; + if (tail_index >= length) + tail_index = 0; + if (test_valid && tail_pos->x () == chop_coord && prev_step.x () < 0) { + if (tail_pos->y () >= chop_starty) { + chop_starty = -MAX_INT16; + test_valid = FALSE; + } + else { + *tail_pos = test_pos; + tail_index = test_index; + break; //must chop there + } + } + if (tail_pos->x () == chop_coord + && srcline->step (tail_index).x () > 0 + && tail_pos->y () > chop_starty) { + chop_starty = tail_pos->y (); + test_index = tail_index; + test_pos = *tail_pos; + test_valid = TRUE; + } + else if (tail_pos->x () == chop_coord + && srcline->step (tail_index).y () < 0 + && prev_step.x () > 0 && tail_pos->y () > chop_starty) + break; //must chop here + } + while (tail_index != startindex + && tail_pos->x () < chop_coord + pitch_error); + return tail_index; +} + + +/********************************************************************** + * next_anti_right_seg + * + * Search the outline for a suitable point at which it crosses the + * chop_coord from right to left. + **********************************************************************/ + +INT16 next_anti_right_seg( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 tail_index, //of tailpos + INT16 startindex, //end of search + INT32 length, //of outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + ICOORD *tail_pos //current position + ) { + BOOL8 test_valid; //test pt valid + INT16 chop_starty; //test chop pt + INT16 test_index; //possible chop pt + ICOORD test_pos; //possible chop pt + ICOORD prev_step; //in x to tail pos + + test_valid = FALSE; + chop_starty = MAX_INT16; + test_index = tail_index; //stop warnings + do { + //move forward + *tail_pos += srcline->step (tail_index); + prev_step = srcline->step (tail_index); + tail_index++; + if (tail_index >= length) + tail_index = 0; + if (test_valid && tail_pos->x () == chop_coord && prev_step.x () > 0) { + if (tail_pos->y () <= chop_starty) { + chop_starty = MAX_INT16; + test_valid = FALSE; + } + else { + *tail_pos = test_pos; + tail_index = test_index; + break; //must chop there + } + } + if (tail_pos->x () == chop_coord + && srcline->step (tail_index).x () < 0 + && tail_pos->y () < chop_starty) { + chop_starty = tail_pos->y (); + test_index = tail_index; + test_pos = *tail_pos; + test_valid = TRUE; //save possible chop pt + } + else if (tail_pos->x () == chop_coord + && srcline->step (tail_index).y () > 0 + && prev_step.x () < 0 && tail_pos->y () < chop_starty) + break; //must chop here + } + while (tail_index != startindex + && tail_pos->x () > chop_coord - pitch_error); + return tail_index; +} + + +/********************************************************************** + * next_clock_left_seg + * + * Search the outline for a suitable point at which it crosses the + * chop_coord from left to right. + **********************************************************************/ + +INT16 next_clock_left_seg( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 tail_index, //of tailpos + INT16 startindex, //end of search + INT32 length, //of outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + ICOORD *tail_pos //current position + ) { + BOOL8 test_valid; //test pt valid + INT16 chop_starty; //test chop pt + INT16 test_index; //possible chop pt + ICOORD test_pos; //possible chop pt + ICOORD prev_step; //in x to tail pos + + test_valid = FALSE; + chop_starty = MAX_INT16; + test_index = tail_index; //stop warnings + do { + *tail_pos += srcline->step (tail_index); + prev_step = srcline->step (tail_index); + tail_index++; + if (tail_index >= length) + tail_index = 0; + if (test_valid && tail_pos->x () == chop_coord && prev_step.x () < 0) { + if (tail_pos->y () <= chop_starty) { + chop_starty = MAX_INT16; + test_valid = FALSE; + } + else { + *tail_pos = test_pos; + tail_index = test_index; + break; //must chop there + } + } + if (tail_pos->x () == chop_coord + && srcline->step (tail_index).x () > 0 + && tail_pos->y () < chop_starty) { + chop_starty = tail_pos->y (); + test_index = tail_index; + test_pos = *tail_pos; + test_valid = TRUE; + } + else if (tail_pos->x () == chop_coord + && srcline->step (tail_index).y () > 0 + && prev_step.x () > 0 && tail_pos->y () < chop_starty) + break; //must chop here + } + while (tail_index != startindex + && tail_pos->x () < chop_coord + pitch_error); + return tail_index; +} + + +/********************************************************************** + * next_clock_right_seg + * + * Search the outline for a suitable point at which it crosses the + * chop_coord from right to left. + **********************************************************************/ + +INT16 next_clock_right_seg( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 tail_index, //of tailpos + INT16 startindex, //end of search + INT32 length, //of outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + ICOORD *tail_pos //current position + ) { + BOOL8 test_valid; //test pt valid + INT16 chop_starty; //test chop pt + INT16 test_index; //possible chop pt + ICOORD test_pos; //possible chop pt + ICOORD prev_step; //in x to tail pos + + test_valid = FALSE; + chop_starty = MAX_INT16; + test_index = tail_index; //stop warnings + do { + //move forward + *tail_pos += srcline->step (tail_index); + prev_step = srcline->step (tail_index); + tail_index++; + if (tail_index >= length) + tail_index = 0; + if (test_valid && tail_pos->x () == chop_coord && prev_step.x () > 0) { + if (tail_pos->y () >= chop_starty) { + chop_starty = MAX_INT16; + test_valid = FALSE; + } + else { + *tail_pos = test_pos; + tail_index = test_index; + break; //must chop there + } + } + if (tail_pos->x () == chop_coord + && srcline->step (tail_index).x () < 0 + && tail_pos->y () > chop_starty) { + chop_starty = tail_pos->y (); + test_index = tail_index; + test_pos = *tail_pos; + test_valid = TRUE; //save possible chop pt + } + else if (tail_pos->x () == chop_coord + && srcline->step (tail_index).y () < 0 + && prev_step.x () < 0 && tail_pos->y () > chop_starty) + break; //must chop here + } + while (tail_index != startindex + && tail_pos->x () > chop_coord - pitch_error); + return tail_index; +} + + +/********************************************************************** + * save_chop_cfragment + * + * Store the given fragment in the given fragment list. + **********************************************************************/ + +void save_chop_cfragment( //chop the outline + INT16 head_index, //head of fragment + ICOORD head_pos, //head of fragment + INT16 tail_index, //tail of fragment + ICOORD tail_pos, //tail of fragment + C_OUTLINE *srcline, //source of edgesteps + C_OUTLINE_FRAG_LIST *frags //fragment list + ) { + INT16 jump; //gap across end + INT16 stepcount; //total steps + C_OUTLINE_FRAG *head; //head of fragment + C_OUTLINE_FRAG *tail; //tail of fragment + INT16 tail_y; //ycoord of tail + + ASSERT_HOST (tail_pos.x () == head_pos.x ()); + ASSERT_HOST (tail_index != head_index); + stepcount = tail_index - head_index; + if (stepcount < 0) + stepcount += srcline->pathlength (); + jump = tail_pos.y () - head_pos.y (); + if (jump < 0) + jump = -jump; + if (jump == stepcount) + return; //its a nop + tail_y = tail_pos.y (); + head = new C_OUTLINE_FRAG (head_pos, tail_pos, srcline, + head_index, tail_index); + tail = new C_OUTLINE_FRAG (head, tail_y); + head->other_end = tail; + add_frag_to_list(head, frags); + add_frag_to_list(tail, frags); +} + + +/********************************************************************** + * C_OUTLINE_FRAG::C_OUTLINE_FRAG + * + * Constructors for C_OUTLINE_FRAG. + **********************************************************************/ + +C_OUTLINE_FRAG::C_OUTLINE_FRAG( //record fragment + ICOORD start_pt, //start coord + ICOORD end_pt, //end coord + C_OUTLINE *outline, //source of steps + INT16 start_index, + INT16 end_index) { + start = start_pt; + end = end_pt; + ycoord = start_pt.y (); + stepcount = end_index - start_index; + if (stepcount < 0) + stepcount += outline->pathlength (); + ASSERT_HOST (stepcount > 0); + steps = new DIR128[stepcount]; + if (end_index > start_index) { + for (int i = start_index; i < end_index; ++i) + steps[i - start_index] = outline->step_dir(i); + } + else { + int len = outline->pathlength(); + int i = start_index; + for (; i < len; ++i) + steps[i - start_index] = outline->step_dir(i); + if (end_index > 0) + for (; i < end_index + len; ++i) + steps[i - start_index] = outline->step_dir(i - len); + } + other_end = NULL; + delete close(); +} + + +C_OUTLINE_FRAG::C_OUTLINE_FRAG( //record fragment + C_OUTLINE_FRAG *head, //other end + INT16 tail_y) { + ycoord = tail_y; + other_end = head; + start = head->start; + end = head->end; + steps = NULL; + stepcount = 0; +} + + +/********************************************************************** + * add_frag_to_list + * + * Insert the fragment in the list at the appropriate place to keep + * them in ascending ycoord order. + **********************************************************************/ + +void add_frag_to_list( //ordered add + C_OUTLINE_FRAG *frag, //fragment to add + C_OUTLINE_FRAG_LIST *frags //fragment list + ) { + //output list + C_OUTLINE_FRAG_IT frag_it = frags; + + if (!frags->empty ()) { + for (frag_it.mark_cycle_pt (); !frag_it.cycled_list (); + frag_it.forward ()) { + if (frag_it.data ()->ycoord > frag->ycoord + || frag_it.data ()->ycoord == frag->ycoord + && frag->other_end->ycoord < frag->ycoord) { + frag_it.add_before_then_move (frag); + return; + } + } + } + frag_it.add_to_end (frag); +} + + +/********************************************************************** + * close_chopped_cfragments + * + * Clear the given list of fragments joining them up into outlines. + * Each outline made soaks up any of the child outlines which it encloses. + **********************************************************************/ + +void close_chopped_cfragments( //chop the outline + C_OUTLINE_FRAG_LIST *frags, //list to clear + C_OUTLINE_LIST *children, //potential children + float pitch_error, //allowed shrinkage + C_OUTLINE_IT *dest_it //output list + ) { + //iterator + C_OUTLINE_FRAG_IT frag_it = frags; + C_OUTLINE_FRAG *bottom_frag; //bottom of cut + C_OUTLINE_FRAG *top_frag; //top of cut + C_OUTLINE *outline; //new outline + C_OUTLINE *child; //current child + C_OUTLINE_IT child_it = children; + C_OUTLINE_IT olchild_it; //children of outline + + while (!frag_it.empty ()) { + frag_it.move_to_first (); + //get bottom one + bottom_frag = frag_it.extract (); + frag_it.forward (); + top_frag = frag_it.data (); //look at next + if (bottom_frag->steps == 0 && top_frag->steps == 0 + || bottom_frag->steps != 0 && top_frag->steps != 0) { + if (frag_it.data_relative (1)->ycoord == top_frag->ycoord) + frag_it.forward (); + } + top_frag = frag_it.extract (); + if (top_frag->other_end != bottom_frag) { + outline = join_chopped_fragments (bottom_frag, top_frag); + ASSERT_HOST (outline == NULL); + } + else { + outline = join_chopped_fragments (bottom_frag, top_frag); + ASSERT_HOST (outline != NULL); + olchild_it.set_to_list (outline->child ()); + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + if (*child < *outline) + olchild_it.add_to_end (child_it.extract ()); + } + if (outline->bounding_box ().width () > pitch_error) + dest_it->add_after_then_move (outline); + else + delete outline; //make it disappear + } + } + while (!child_it.empty ()) { + dest_it->add_after_then_move (child_it.extract ()); + child_it.forward (); + } +} + + +/********************************************************************** + * join_chopped_fragments + * + * Join the two lists of POLYPTs such that neither OUTLINE_FRAG + * operand keeps responsibility for the fragment. + **********************************************************************/ + +C_OUTLINE *join_chopped_fragments( //join pieces + C_OUTLINE_FRAG *bottom, //bottom of cut + C_OUTLINE_FRAG *top //top of cut + ) { + C_OUTLINE *outline; //closed loop + + if (bottom->other_end == top) { + if (bottom->steps == 0) + outline = top->close (); //turn to outline + else + outline = bottom->close (); + delete top; + delete bottom; + return outline; + } + if (bottom->steps == 0) { + ASSERT_HOST (top->steps != 0); + join_segments (bottom->other_end, top); + } + else { + ASSERT_HOST (top->steps == 0); + join_segments (top->other_end, bottom); + } + top->other_end->other_end = bottom->other_end; + bottom->other_end->other_end = top->other_end; + delete bottom; + delete top; + return NULL; +} + + +/********************************************************************** + * join_segments + * + * Join the two edgestep fragments such that the second comes after + * the first and the gap beween them is closed. + **********************************************************************/ + +void join_segments( //join pieces + C_OUTLINE_FRAG *bottom, //bottom of cut + C_OUTLINE_FRAG *top //top of cut + ) { + DIR128 *steps; //new steps + INT32 stepcount; //no of steps + INT16 fake_count; //fake steps + DIR128 fake_step; //step entry + + ASSERT_HOST (bottom->end.x () == top->start.x ()); + fake_count = top->start.y () - bottom->end.y (); + if (fake_count < 0) { + fake_count = -fake_count; + fake_step = 32; + } + else + fake_step = 96; + + stepcount = bottom->stepcount + fake_count + top->stepcount; + steps = new DIR128[stepcount]; + memmove (steps, bottom->steps, bottom->stepcount); + memset (steps + bottom->stepcount, fake_step.get_dir(), fake_count); + memmove (steps + bottom->stepcount + fake_count, top->steps, + top->stepcount); + delete [] bottom->steps; + bottom->steps = steps; + bottom->stepcount = stepcount; + bottom->end = top->end; + bottom->other_end->end = top->end; +} + + +/********************************************************************** + * C_OUTLINE_FRAG::close + * + * Join the ends of this fragment and turn it into an outline. + **********************************************************************/ + +C_OUTLINE *C_OUTLINE_FRAG::close() { //join pieces + DIR128 *new_steps; //new steps + INT32 new_stepcount; //no of steps + INT16 fake_count; //fake steps + DIR128 fake_step; //step entry + + ASSERT_HOST (start.x () == end.x ()); + fake_count = start.y () - end.y (); + if (fake_count < 0) { + fake_count = -fake_count; + fake_step = 32; + } + else + fake_step = 96; + + new_stepcount = stepcount + fake_count; + new_steps = new DIR128[new_stepcount]; + memmove(new_steps, steps, stepcount); + memset (new_steps + stepcount, fake_step.get_dir(), fake_count); + C_OUTLINE* result = new C_OUTLINE (start, new_steps, new_stepcount); + delete [] new_steps; + return result; +} + + +/********************************************************************** + * C_OUTLINE_FRAG::operator= + * + * Copy this fragment. + **********************************************************************/ + + //join pieces +C_OUTLINE_FRAG & C_OUTLINE_FRAG::operator= ( +const C_OUTLINE_FRAG & src //fragment to copy +) { + if (steps != NULL) + delete [] steps; + + stepcount = src.stepcount; + steps = new DIR128[stepcount]; + memmove (steps, src.steps, stepcount); + start = src.start; + end = src.end; + ycoord = src.ycoord; + return *this; +} diff --git a/textord/fpchop.h b/textord/fpchop.h new file mode 100644 index 0000000000..98091f2363 --- /dev/null +++ b/textord/fpchop.h @@ -0,0 +1,238 @@ +/********************************************************************** + * File: fpchop.h (Formerly fp_chop.h) + * Description: Code to chop fixed pitch text into character cells. + * Author: Ray Smith + * Created: Thu Sep 16 11:14:15 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef FPCHOP_H +#define FPCHOP_H + +#include "varable.h" +#include "blobbox.h" +#include "notdll.h" +#include "notdll.h" + +class OUTLINE_FRAG:public ELIST_LINK +{ + public: + OUTLINE_FRAG() { + } //empty constructor + //head of fragment + OUTLINE_FRAG(POLYPT_IT *head_it, POLYPT_IT *tail_it); //tail of fragment + //other end + OUTLINE_FRAG(OUTLINE_FRAG *head, float tail_y); + + POLYPT_LIST polypts; //only if a head + OUTLINE_FRAG *other_end; //head if a tail + float ycoord; //coord of cut pt + + private: +}; + +class C_OUTLINE_FRAG:public ELIST_LINK +{ + public: + C_OUTLINE_FRAG() { //empty constructor + steps = NULL; + stepcount = 0; + } + ~C_OUTLINE_FRAG () { + if (steps != NULL) + delete [] steps; + } + //start coord + C_OUTLINE_FRAG(ICOORD start_pt, + ICOORD end_pt, //end coord + C_OUTLINE *outline, //source of steps + INT16 start_index, + INT16 end_index); + //other end + C_OUTLINE_FRAG(C_OUTLINE_FRAG *head, INT16 tail_y); + C_OUTLINE *close(); //copy to outline + C_OUTLINE_FRAG & operator= ( //assign + const C_OUTLINE_FRAG & src); + + ICOORD start; //start coord + ICOORD end; //end coord + DIR128 *steps; //step array + INT32 stepcount; //no of steps + C_OUTLINE_FRAG *other_end; //head if a tail + INT16 ycoord; //coord of cut pt + + private: +}; + +ELISTIZEH (OUTLINE_FRAG) ELISTIZEH (C_OUTLINE_FRAG) +extern +INT_VAR_H (textord_fp_chop_error, 2, +"Max allowed bending of chop cells"); +extern +double_VAR_H (textord_fp_chop_snap, 0.5, +"Max distance of chop pt from vertex"); +ROW *fixed_pitch_words( //find lines + TO_ROW *row, //row to do + FCOORD rotation //for drawing + ); +WERD *add_repeated_word( //move repeated word + WERD_IT *rep_it, //repeated words + INT16 &rep_left, //left edge of word + INT16 &prev_chop_coord, //previous word end + UINT8 &blanks, //no of blanks + float pitch, //char cell size + WERD_IT *word_it //list of words + ); +void split_to_blob( //split the blob + BLOBNBOX *blob, //blob to split + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + OUTLINE_LIST *left_outlines, //left half of chop + C_OUTLINE_LIST *left_coutlines, //for cblobs + OUTLINE_LIST *right_outlines, //right half of chop + C_OUTLINE_LIST *right_coutlines); +void fixed_chop_blob( //split the blob + PBLOB *blob, //blob to split + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + OUTLINE_LIST *left_outlines, //left half of chop + OUTLINE_LIST *right_outlines //right half of chop + ); +void fixed_split_outline( //chop the outline + OUTLINE *srcline, //source outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + OUTLINE_IT *left_it, //left half of chop + OUTLINE_IT *right_it //right half of chop + ); +BOOL8 fixed_chop_outline( //chop the outline + OUTLINE *srcline, //source outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + OUTLINE_FRAG_LIST *left_frags, //left half of chop + OUTLINE_FRAG_LIST *right_frags //right half of chop + ); +void save_chop_fragment( //chop the outline + POLYPT_IT *head_it, //head of fragment + POLYPT_IT *tail_it, //tail of fragment + OUTLINE_FRAG_LIST *frags //fragment list + ); +void add_frag_to_list( //ordered add + OUTLINE_FRAG *frag, //fragment to add + OUTLINE_FRAG_LIST *frags //fragment list + ); +void insert_chop_pt( //make chop + POLYPT_IT *it, //iterator + INT16 chop_coord //required chop pt + ); +FCOORD find_chop_coords( //make chop + POLYPT_IT *it, //iterator + INT16 chop_coord //required chop pt + ); +void insert_extra_pt( //make extra + POLYPT_IT *it //iterator + ); +void close_chopped_fragments( //chop the outline + OUTLINE_FRAG_LIST *frags, //list to clear + OUTLINE_LIST *children, //potential children + OUTLINE_IT *dest_it //output list + ); +void join_chopped_fragments( //join pieces + OUTLINE_FRAG *bottom, //bottom of cut + OUTLINE_FRAG *top //top of cut + ); +void fixed_chop_cblob( //split the blob + C_BLOB *blob, //blob to split + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + C_OUTLINE_LIST *left_outlines, //left half of chop + C_OUTLINE_LIST *right_outlines //right half of chop + ); +void fixed_split_coutline( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + C_OUTLINE_IT *left_it, //left half of chop + C_OUTLINE_IT *right_it //right half of chop + ); +BOOL8 fixed_chop_coutline( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + C_OUTLINE_FRAG_LIST *left_frags, //left half of chop + C_OUTLINE_FRAG_LIST *right_frags //right half of chop + ); +INT16 next_anti_left_seg( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 tail_index, //of tailpos + INT16 startindex, //end of search + INT32 length, //of outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + ICOORD *tail_pos //current position + ); +INT16 next_anti_right_seg( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 tail_index, //of tailpos + INT16 startindex, //end of search + INT32 length, //of outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + ICOORD *tail_pos //current position + ); +INT16 next_clock_left_seg( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 tail_index, //of tailpos + INT16 startindex, //end of search + INT32 length, //of outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + ICOORD *tail_pos //current position + ); +INT16 next_clock_right_seg( //chop the outline + C_OUTLINE *srcline, //source outline + INT16 tail_index, //of tailpos + INT16 startindex, //end of search + INT32 length, //of outline + INT16 chop_coord, //place to chop + float pitch_error, //allowed deviation + ICOORD *tail_pos //current position + ); +void save_chop_cfragment( //chop the outline + INT16 head_index, //head of fragment + ICOORD head_pos, //head of fragment + INT16 tail_index, //tail of fragment + ICOORD tail_pos, //tail of fragment + C_OUTLINE *srcline, //source of edgesteps + C_OUTLINE_FRAG_LIST *frags //fragment list + ); +void add_frag_to_list( //ordered add + C_OUTLINE_FRAG *frag, //fragment to add + C_OUTLINE_FRAG_LIST *frags //fragment list + ); +void close_chopped_cfragments( //chop the outline + C_OUTLINE_FRAG_LIST *frags, //list to clear + C_OUTLINE_LIST *children, //potential children + float pitch_error, //allowed shrinkage + C_OUTLINE_IT *dest_it //output list + ); +C_OUTLINE *join_chopped_fragments( //join pieces + C_OUTLINE_FRAG *bottom, //bottom of cut + C_OUTLINE_FRAG *top //top of cut + ); +void join_segments( //join pieces + C_OUTLINE_FRAG *bottom, //bottom of cut + C_OUTLINE_FRAG *top //top of cut + ); +#endif diff --git a/textord/gap_map.cpp b/textord/gap_map.cpp new file mode 100644 index 0000000000..26d35a90a1 --- /dev/null +++ b/textord/gap_map.cpp @@ -0,0 +1,166 @@ +#include "mfcpch.h" +#include "statistc.h" +#include "gap_map.h" + +#define EXTERN +EXTERN BOOL_VAR (gapmap_debug, FALSE, "Say which blocks have tables"); +EXTERN BOOL_VAR (gapmap_use_ends, FALSE, +"Use large space at start and end of rows"); +EXTERN BOOL_VAR (gapmap_no_isolated_quanta, FALSE, +"Ensure gaps not less than 2quanta wide"); +EXTERN double_VAR (gapmap_big_gaps, 1.75, "xht multiplier"); + +/************************************************************************* + * A block gap map is a quantised histogram of whitespace regions in the + * block. It is a vertical projection of wide gaps WITHIN lines + * + * The map is held as an array of counts of rows which have a wide gap + * covering that region of the row. Each bucket in the map represents a width + * of about half an xheight - (The median of the xhts in the rows is used.) + * + * The block is considered RECTANGULAR - delimited by the left and right + * extremes of the rows in the block. However, ONLY wide gaps WITHIN a row are + * counted. + * + *************************************************************************/ + +GAPMAP::GAPMAP( //Constructor + TO_BLOCK *block //block + ) { + TO_ROW_IT row_it; //row iterator + TO_ROW *row; //current row + BLOBNBOX_IT blob_it; //iterator + BOX blob_box; + BOX prev_blob_box; + INT16 gap_width; + INT16 start_of_row; + INT16 end_of_row; + STATS xht_stats (0, 128); + INT16 min_quantum; + INT16 max_quantum; + INT16 i; + + row_it.set_to_list (block->get_rows ()); + /* + Find left and right extremes and bucket size + */ + map = NULL; + min_left = MAX_INT16; + max_right = -MAX_INT16; + total_rows = 0; + any_tabs = FALSE; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (!row->blob_list ()->empty ()) { + total_rows++; + xht_stats.add ((INT16) floor (row->xheight + 0.5), 1); + blob_it.set_to_list (row->blob_list ()); + start_of_row = blob_it.data ()->bounding_box ().left (); + end_of_row = blob_it.data_relative (-1)->bounding_box ().right (); + if (min_left > start_of_row) + min_left = start_of_row; + if (max_right < end_of_row) + max_right = end_of_row; + } + } + if ((total_rows < 3) || (min_left >= max_right)) { + total_rows = 0; + min_left = max_right = 0; + return; + } + bucket_size = (INT16) floor (xht_stats.median () + 0.5) / 2; + map_max = (max_right - min_left) / bucket_size; + map = (INT16 *) alloc_mem ((map_max + 1) * sizeof (INT16)); + for (i = 0; i <= map_max; i++) + map[i] = 0; + + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (!row->blob_list ()->empty ()) { + blob_it.set_to_list (row->blob_list ()); + blob_it.mark_cycle_pt (); + blob_box = box_next (&blob_it); + prev_blob_box = blob_box; + if (gapmap_use_ends) { + /* Leading space */ + gap_width = blob_box.left () - min_left; + if ((gap_width > gapmap_big_gaps * row->xheight) + && gap_width > 2) { + max_quantum = (blob_box.left () - min_left) / bucket_size; + for (i = 0; i <= max_quantum; i++) + map[i]++; + } + } + while (!blob_it.cycled_list ()) { + blob_box = box_next (&blob_it); + gap_width = blob_box.left () - prev_blob_box.right (); + if ((gap_width > gapmap_big_gaps * row->xheight) + && gap_width > 2) { + min_quantum = + (prev_blob_box.right () - min_left) / bucket_size; + max_quantum = (blob_box.left () - min_left) / bucket_size; + for (i = min_quantum; i <= max_quantum; i++) + map[i]++; + } + prev_blob_box = blob_box; + } + if (gapmap_use_ends) { + /* Trailing space */ + gap_width = max_right - prev_blob_box.right (); + if ((gap_width > gapmap_big_gaps * row->xheight) + && gap_width > 2) { + min_quantum = + (prev_blob_box.right () - min_left) / bucket_size; + for (i = min_quantum; i <= map_max; i++) + map[i]++; + } + } + } + } + for (i = 0; i <= map_max; i++) { + if (map[i] > total_rows / 2) { + if (gapmap_no_isolated_quanta && + (((i == 0) && + (map[i + 1] <= total_rows / 2)) || + ((i == map_max) && + (map[i - 1] <= total_rows / 2)) || + ((i > 0) && + (i < map_max) && + (map[i - 1] <= total_rows / 2) && + (map[i + 1] <= total_rows / 2)))) { + map[i] = 0; //prevent isolated quantum + } + else + any_tabs = TRUE; + } + } + if (gapmap_debug && any_tabs) + tprintf ("Table found\n"); +} + + +/************************************************************************* + * GAPMAP::table_gap() + * Is there a bucket in the specified range where more than half the rows in the + * block have a wide gap? + *************************************************************************/ + +BOOL8 GAPMAP::table_gap( //Is gap a table? + INT16 left, //From here + INT16 right //To here + ) { + INT16 min_quantum; + INT16 max_quantum; + INT16 i; + BOOL8 tab_found = FALSE; + + if (!any_tabs) + return FALSE; + + min_quantum = (left - min_left) / bucket_size; + max_quantum = (right - min_left) / bucket_size; + for (i = min_quantum; (!tab_found && (i <= max_quantum)); i++) + if (map[i] > total_rows / 2) + tab_found = TRUE; + return tab_found; +} diff --git a/textord/gap_map.h b/textord/gap_map.h new file mode 100644 index 0000000000..5f97874f82 --- /dev/null +++ b/textord/gap_map.h @@ -0,0 +1,40 @@ +#ifndef GAP_MAP_H +#define GAP_MAP_H + +#include "blobbox.h" +#include "notdll.h" + +class GAPMAP +{ + public: + GAPMAP( //constructor + TO_BLOCK *block); + + ~GAPMAP () { //destructor + if (map != NULL) + free_mem(map); + } + + BOOL8 table_gap( //Is gap a table? + INT16 left, //From here + INT16 right); //To here + + private: + INT16 total_rows; //in block + INT16 min_left; //Left extreme + INT16 max_right; //Right extreme + INT16 bucket_size; // half an x ht + INT16 *map; //empty counts + INT16 map_max; //map[0..max_map] defind + BOOL8 any_tabs; +}; + +/*-----------------------------*/ + +extern BOOL_VAR_H (gapmap_debug, FALSE, "Say which blocks have tables"); +extern BOOL_VAR_H (gapmap_use_ends, FALSE, +"Use large space at start and end of rows"); +extern BOOL_VAR_H (gapmap_no_isolated_quanta, FALSE, +"Ensure gaps not less than 2quanta wide"); +extern double_VAR_H (gapmap_big_gaps, 1.75, "xht multiplier"); +#endif diff --git a/textord/makerow.cpp b/textord/makerow.cpp new file mode 100644 index 0000000000..cbea92b885 --- /dev/null +++ b/textord/makerow.cpp @@ -0,0 +1,2583 @@ +/********************************************************************** + * File: makerow.cpp (Formerly makerows.c) + * Description: Code to arrange blobs into rows of text. + * Author: Ray Smith + * Created: Mon Sep 21 14:34:48 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include "stderr.h" +#include "blobbox.h" +#include "lmedsq.h" +#include "statistc.h" +#include "drawtord.h" +#include "blkocc.h" +#include "sortflts.h" +#include "oldbasel.h" +#include "tordmain.h" +#include "underlin.h" +#include "makerow.h" +#include "tprintf.h" + +#define EXTERN + +EXTERN BOOL_VAR (textord_heavy_nr, FALSE, "Vigorously remove noise"); +EXTERN BOOL_VAR (textord_show_initial_rows, FALSE, +"Display row accumulation"); +EXTERN BOOL_VAR (textord_show_parallel_rows, FALSE, +"Display page correlated rows"); +EXTERN BOOL_VAR (textord_show_expanded_rows, FALSE, +"Display rows after expanding"); +EXTERN BOOL_VAR (textord_show_final_rows, FALSE, +"Display rows after final fittin"); +EXTERN BOOL_VAR (textord_show_final_blobs, FALSE, +"Display blob bounds after pre-ass"); +EXTERN BOOL_VAR (textord_test_landscape, FALSE, "Tests refer to land/port"); +EXTERN BOOL_VAR (textord_parallel_baselines, TRUE, +"Force parallel baselines"); +EXTERN BOOL_VAR (textord_straight_baselines, FALSE, +"Force straight baselines"); +EXTERN BOOL_VAR (textord_quadratic_baselines, FALSE, "Use quadratic splines"); +EXTERN BOOL_VAR (textord_old_baselines, TRUE, "Use old baseline algorithm"); +EXTERN BOOL_VAR (textord_old_xheight, TRUE, "Use old xheight algorithm"); +EXTERN BOOL_VAR (textord_fix_xheight_bug, TRUE, "Use spline baseline"); +EXTERN BOOL_VAR (textord_fix_makerow_bug, TRUE, "Prevent multiple baselines"); +EXTERN BOOL_VAR (textord_row_xheights, FALSE, "Use row height policy"); +EXTERN BOOL_VAR (textord_block_xheights, TRUE, "Use block height policy"); +EXTERN BOOL_VAR (textord_xheight_tweak, FALSE, "New min condition on height"); +EXTERN BOOL_VAR (textord_cblob_blockocc, TRUE, +"Use new projection for underlines"); +EXTERN BOOL_VAR (textord_debug_xheights, FALSE, "Test xheight algorithms"); +EXTERN BOOL_VAR (textord_biased_skewcalc, TRUE, +"Bias skew estimates with line length"); +EXTERN BOOL_VAR (textord_interpolating_skew, TRUE, "Interpolate across gaps"); +EXTERN INT_VAR (textord_skewsmooth_offset, 2, "For smooth factor"); +EXTERN INT_VAR (textord_test_x, 0, "coord of test pt"); +EXTERN INT_VAR (textord_test_y, 0, "coord of test pt"); +EXTERN INT_VAR (textord_min_blobs_in_row, 4, +"Min blobs before gradient counted"); +EXTERN INT_VAR (textord_spline_minblobs, 8, +"Min blobs in each spline segment"); +EXTERN INT_VAR (textord_spline_medianwin, 6, +"Size of window for spline segmentation"); +EXTERN INT_VAR (textord_min_xheight, 10, "Min credible pixel xheight"); +EXTERN double_VAR (textord_spline_shift_fraction, 0.02, +"Fraction of line spacing for quad"); +EXTERN double_VAR (textord_spline_outlier_fraction, 0.1, +"Fraction of line spacing for outlier"); +EXTERN double_VAR (textord_skew_ile, 0.5, "Ile of gradients for page skew"); +EXTERN double_VAR (textord_skew_lag, 0.01, +"Lag for skew on row accumulation"); +EXTERN double_VAR (textord_linespace_iqrlimit, 0.2, +"Max iqr/median for linespace"); +EXTERN double_VAR (textord_width_limit, 8, "Max width of blobs to make rows"); +EXTERN double_VAR (textord_chop_width, 1.5, "Max width before chopping"); +EXTERN double_VAR (textord_merge_desc, 0.25, +"Fraction of linespace for desc drop"); +EXTERN double_VAR (textord_merge_x, 0.5, +"Fraction of linespace for x height"); +EXTERN double_VAR (textord_merge_asc, 0.25, +"Fraction of linespace for asc height"); +EXTERN double_VAR (textord_minxh, 0.25, +"fraction of linesize for min xheight"); +EXTERN double_VAR (textord_min_linesize, 1.25, +"* blob height for initial linesize"); +EXTERN double_VAR (textord_excess_blobsize, 1.3, +"New row made if blob makes row this big"); +EXTERN double_VAR (textord_occupancy_threshold, 0.4, +"Fraction of neighbourhood"); +EXTERN double_VAR (textord_underline_width, 2.0, +"Multiple of line_size for underline"); +EXTERN double_VAR (textord_xheight_mode_fraction, 0.4, +"Min pile height to make xheight"); +EXTERN double_VAR (textord_ascheight_mode_fraction, 0.15, +"Min pile height to make ascheight"); +EXTERN double_VAR (textord_ascx_ratio_min, 1.2, "Min cap/xheight"); +EXTERN double_VAR (textord_ascx_ratio_max, 1.7, "Max cap/xheight"); +EXTERN double_VAR (textord_descx_ratio_min, 0.15, "Min desc/xheight"); +EXTERN double_VAR (textord_descx_ratio_max, 0.6, "Max desc/xheight"); +EXTERN double_VAR (textord_xheight_error_margin, 0.1, "Accepted variation"); + +#define MAX_HEIGHT_MODES 12 + +/********************************************************************** + * make_rows + * + * Arrange the blobs into rows. + **********************************************************************/ + +float make_rows( //make rows + ICOORD page_tr, //top right + BLOCK_LIST *blocks, //block list + TO_BLOCK_LIST *land_blocks, //rotated for landscape + TO_BLOCK_LIST *port_blocks //output list + ) { + float port_m; //global skew + float port_err; //global noise + // float land_m; //global skew + // float land_err; //global noise + TO_BLOCK_IT block_it; //iterator + + //don't do landscape for now + // block_it.set_to_list(land_blocks); + // for (block_it.mark_cycle_pt();!block_it.cycled_list();block_it.forward()) + // make_initial_textrows(page_tr,block_it.data(),FCOORD(0,-1), + // (BOOL8)textord_test_landscape); + block_it.set_to_list (port_blocks); + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) + make_initial_textrows (page_tr, block_it.data (), FCOORD (1.0f, 0.0f), + !(BOOL8) textord_test_landscape); + //compute globally + compute_page_skew(port_blocks, port_m, port_err); + // compute_page_skew(land_blocks,land_m,land_err); //compute globally + // tprintf("Portrait skew gradient=%g, error=%g.\n", + // port_m,port_err); + // tprintf("Landscape skew gradient=%g, error=%g.\n", + // land_m,land_err); + block_it.set_to_list (port_blocks); + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + cleanup_rows (page_tr, block_it.data (), port_m, FCOORD (1.0f, 0.0f), + block_it.data ()->block->bounding_box ().left (), + !(BOOL8) textord_test_landscape); + } + block_it.set_to_list (land_blocks); + // for (block_it.mark_cycle_pt();!block_it.cycled_list();block_it.forward()) + // { + // cleanup_rows(page_tr,block_it.data(),land_m,FCOORD(0,-1), + // -block_it.data()->block->bounding_box().top(), + // (BOOL8)textord_test_landscape); + // } + return port_m; //global skew +} + + +/********************************************************************** + * make_initial_textrows + * + * Arrange the good blobs into rows of text. + **********************************************************************/ + +void make_initial_textrows( //find lines + ICOORD page_tr, + TO_BLOCK *block, //block to do + FCOORD rotation, //for drawing + BOOL8 testing_on //correct orientation + ) { + TO_ROW_IT row_it = block->get_rows (); + +#ifndef GRAPHICS_DISABLED + COLOUR colour; //of row + + if (textord_show_initial_rows && testing_on) { + if (to_win == NO_WINDOW) + create_to_win(page_tr); + } +#endif + //guess skew + assign_blobs_to_rows (block, NULL, 0, TRUE, TRUE, textord_show_initial_rows && testing_on); + row_it.move_to_first (); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) + fit_lms_line (row_it.data ()); +#ifndef GRAPHICS_DISABLED + if (textord_show_initial_rows && testing_on) { + colour = RED; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + plot_to_row (row_it.data (), colour, rotation); + colour = (COLOUR) (colour + 1); + if (colour > MAGENTA) + colour = RED; + } + } +#endif +} + + +/********************************************************************** + * fit_lms_line + * + * Fit an LMS line to a row. + **********************************************************************/ + +void fit_lms_line( //sort function + TO_ROW *row //row to fit + ) { + float m, c; //fitted line + BOX box; //blob box + LMS lms (row->blob_list ()->length ()); + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + box = blob_it.data ()->bounding_box (); + lms.add (FCOORD ((box.left () + box.right ()) / 2.0, box.bottom ())); + } + lms.fit (m, c); + row->set_line (m, c, lms.error ()); +} + + +/********************************************************************** + * compute_page_skew + * + * Compute the skew over a full page by averaging the gradients over + * all the lines. Get the error of the same row. + **********************************************************************/ + +void compute_page_skew( //get average gradient + TO_BLOCK_LIST *blocks, //list of blocks + float &page_m, //average gradient + float &page_err //average error + ) { + INT32 row_count; //total rows + INT32 blob_count; //total_blobs + INT32 row_err; //integer error + float *gradients; //of rows + float *errors; //of rows + INT32 row_index; //of total + TO_ROW *row; //current row + TO_BLOCK_IT block_it = blocks; //iterator + TO_ROW_IT row_it; + + row_count = 0; + blob_count = 0; + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + row_count += block_it.data ()->get_rows ()->length (); + //count up rows + row_it.set_to_list (block_it.data ()->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) + blob_count += row_it.data ()->blob_list ()->length (); + } + if (row_count == 0) { + page_m = 0.0f; + page_err = 0.0f; + return; + } + gradients = (float *) alloc_mem (blob_count * sizeof (float)); + //get mem + errors = (float *) alloc_mem (blob_count * sizeof (float)); + if (gradients == NULL || errors == NULL) + MEMORY_OUT.error ("compute_page_skew", ABORT, NULL); + + row_index = 0; + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + row_it.set_to_list (block_it.data ()->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + blob_count = row->blob_list ()->length (); + row_err = (INT32) ceil (row->line_error ()); + if (row_err <= 0) + row_err = 1; + if (textord_biased_skewcalc) { + blob_count /= row_err; + for (blob_count /= row_err; blob_count > 0; blob_count--) { + gradients[row_index] = row->line_m (); + errors[row_index] = row->line_error (); + row_index++; + } + } + else if (blob_count >= textord_min_blobs_in_row) { + //get gradient + gradients[row_index] = row->line_m (); + errors[row_index] = row->line_error (); + row_index++; + } + } + } + if (row_index == 0) { + //desperate + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + row_it.set_to_list (block_it.data ()->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); + row_it.forward ()) { + row = row_it.data (); + gradients[row_index] = row->line_m (); + errors[row_index] = row->line_error (); + row_index++; + } + } + } + row_count = row_index; + row_index = choose_nth_item ((INT32) (row_count * textord_skew_ile), + gradients, row_count); + page_m = gradients[row_index]; + row_index = choose_nth_item ((INT32) (row_count * textord_skew_ile), + errors, row_count); + page_err = errors[row_index]; + free_mem(gradients); + free_mem(errors); +} + +const double kNoiseSize = 0.5; // Fraction of xheight. +const int kMinSize = 8; // Min pixels to be xheight. + +// Return true if the dot looks like it is part of the i. +// Doesn't work for any other diacritical. +static bool dot_of_i(BLOBNBOX* dot, BLOBNBOX* i, TO_ROW* row) { + const BOX& ibox = i->bounding_box(); + const BOX& dotbox = dot->bounding_box(); + + // Must overlap horizontally by enough and be high enough. + int overlap = MIN(dotbox.right(), ibox.right()) - + MAX(dotbox.left(), ibox.left()); + if (ibox.height() <= 2 * dotbox.height() || + (overlap * 2 < ibox.width() && overlap < dotbox.width())) + return false; + + // If the i is tall and thin then it is good. + if (ibox.height() > ibox.width() * 2) + return true; // The i or ! must be tall and thin. + + // It might still be tall and thin, but it might be joined to something. + // So search the outline for a piece of large height close to the edges + // of the dot. + const double kHeightFraction = 0.6; + double target_height = MIN(dotbox.bottom(), ibox.top()); + target_height -= row->line_m()*dotbox.left() + row->line_c(); + target_height *= kHeightFraction; + int left_min = dotbox.left() - dotbox.width(); + int middle = (dotbox.left() + dotbox.right())/2; + int right_max = dotbox.right() + dotbox.width(); + int left_miny = 0; + int left_maxy = 0; + int right_miny = 0; + int right_maxy = 0; + bool found_left = false; + bool found_right = false; + bool in_left = false; + bool in_right = false; + C_BLOB* blob = i->cblob(); + C_OUTLINE_IT o_it = blob->out_list(); + for (o_it.mark_cycle_pt(); !o_it.cycled_list(); o_it.forward()) { + C_OUTLINE* outline = o_it.data(); + int length = outline->pathlength(); + ICOORD pos = outline->start_pos(); + for (int step = 0; step < length; pos += outline->step(step++)) { + int x = pos.x(); + int y = pos.y(); + if (x >= left_min && x < middle && !found_left) { + // We are in the left part so find min and max y. + if (in_left) { + if (y > left_maxy) left_maxy = y; + if (y < left_miny) left_miny = y; + } else { + left_maxy = left_miny = y; + in_left = true; + } + } else if (in_left) { + // We just left the left so look for size. + if (left_maxy - left_miny > target_height) { + if (found_right) + return true; + found_left = true; + } + in_left = false; + } + if (x <= right_max && x > middle && !found_right) { + // We are in the right part so find min and max y. + if (in_right) { + if (y > right_maxy) right_maxy = y; + if (y < right_miny) right_miny = y; + } else { + right_maxy = right_miny = y; + in_right = true; + } + } else if (in_right) { + // We just left the right so look for size. + if (right_maxy - right_miny > target_height) { + if (found_left) + return true; + found_right = true; + } + in_right = false; + } + } + } + return false; +} + +static void vigorous_noise_removal(TO_BLOCK* block) { + TO_ROW_IT row_it = block->get_rows (); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + TO_ROW* row = row_it.data(); + BLOBNBOX_IT b_it = row->blob_list(); + // Estimate the xheight on the row. + int max_height = 0; + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + BLOBNBOX* blob = b_it.data(); + if (blob->bounding_box().height() > max_height) + max_height = blob->bounding_box().height(); + } + STATS hstats(0, max_height + 1); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + BLOBNBOX* blob = b_it.data(); + int height = blob->bounding_box().height(); + if (height >= kMinSize) + hstats.add(blob->bounding_box().height(), 1); + } + float xheight = hstats.median(); + // Delete small objects. + BLOBNBOX* prev = NULL; + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + BLOBNBOX* blob = b_it.data(); + const BOX& box = blob->bounding_box(); + if (box.height() < kNoiseSize * xheight) { + // Small so delete unless it looks like an i dot. + if (prev != NULL) { + if (dot_of_i(blob, prev, row)) + continue; // Looks OK. + } + if (!b_it.at_last()) { + BLOBNBOX* next = b_it.data_relative(1); + if (dot_of_i(blob, next, row)) + continue; // Looks OK. + } + // It might be noise so get rid of it. + if (blob->blob() != NULL) + delete blob->blob(); + if (blob->cblob() != NULL) + delete blob->cblob(); + delete b_it.extract(); + } else { + prev = blob; + } + } + } +} + +/********************************************************************** + * cleanup_rows + * + * Remove overlapping rows and fit all the blobs to what's left. + **********************************************************************/ + +void cleanup_rows( //find lines + ICOORD page_tr, //top right + TO_BLOCK *block, //block to do + float gradient, //gradient to fit + FCOORD rotation, //for drawing + INT32 block_edge, //edge of block + BOOL8 testing_on //correct orientation + ) { + //iterators + BLOBNBOX_IT blob_it = &block->blobs; + TO_ROW_IT row_it = block->get_rows (); + +#ifndef GRAPHICS_DISABLED + if (textord_show_parallel_rows && testing_on) { + if (to_win == NO_WINDOW) + create_to_win(page_tr); + } +#endif + //get row coords + fit_parallel_rows(block, + gradient, + rotation, + block_edge, + textord_show_parallel_rows &&testing_on); + delete_non_dropout_rows(block, + gradient, + rotation, + block_edge, + textord_show_parallel_rows &&testing_on); + expand_rows(page_tr, block, gradient, rotation, block_edge, testing_on); + blob_it.set_to_list (&block->blobs); + row_it.set_to_list (block->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) + blob_it.add_list_after (row_it.data ()->blob_list ()); + //give blobs back + assign_blobs_to_rows (block, &gradient, 1, TRUE, TRUE, FALSE); + //now new rows must be genuine + blob_it.set_to_list (&block->blobs); + blob_it.add_list_after (&block->large_blobs); + assign_blobs_to_rows (block, &gradient, 2, TRUE, TRUE, FALSE); + //safe to use big ones now + blob_it.set_to_list (&block->blobs); + //throw all blobs in + blob_it.add_list_after (&block->noise_blobs); + blob_it.add_list_after (&block->small_blobs); + assign_blobs_to_rows (block, &gradient, 3, FALSE, FALSE, FALSE); + //no rows for noise + row_it.set_to_list (block->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) + row_it.data ()->blob_list ()->sort (blob_x_order); + fit_parallel_rows(block, gradient, rotation, block_edge, FALSE); + if (textord_heavy_nr) { + vigorous_noise_removal(block); + } + separate_underlines(block, gradient, rotation, testing_on); + pre_associate_blobs(page_tr, block, rotation, testing_on); + +#ifndef GRAPHICS_DISABLED + if (textord_show_final_rows && testing_on) { + if (to_win == NO_WINDOW) + create_to_win(page_tr); + } +#endif + + fit_parallel_rows(block, gradient, rotation, block_edge, FALSE); + // textord_show_final_rows && testing_on); + make_spline_rows(block, + gradient, + rotation, + block_edge, + textord_show_final_rows &&testing_on); + if (!textord_old_xheight || !textord_old_baselines) + compute_block_xheight(block, gradient); + if (textord_restore_underlines) + //fix underlines + restore_underlined_blobs(block); +#ifndef GRAPHICS_DISABLED + if (textord_show_final_rows && testing_on) { + plot_blob_list (to_win, &block->blobs, MAGENTA, WHITE); + //show discarded blobs + plot_blob_list (to_win, &block->underlines, YELLOW, CORAL); + } + if (textord_show_final_rows && testing_on && block->blobs.length () > 0) + tprintf ("%d blobs discarded as noise\n", block->blobs.length ()); + if (textord_show_final_rows && testing_on) { + draw_meanlines(block, gradient, block_edge, WHITE, rotation); + } +#endif +} + + +/********************************************************************** + * delete_non_dropout_rows + * + * Compute the linespacing and offset. + **********************************************************************/ + +void delete_non_dropout_rows( //find lines + TO_BLOCK *block, //block to do + float gradient, //global skew + FCOORD rotation, //deskew vector + INT32 block_edge, //left edge + BOOL8 testing_on //correct orientation + ) { + BOX block_box; //deskewed block + INT32 *deltas; //change in occupation + INT32 *occupation; //of pixel coords + INT32 max_y; //in block + INT32 min_y; + INT32 line_index; //of scan line + INT32 line_count; //no of scan lines + INT32 distance; //to drop-out + INT32 xleft; //of block + INT32 ybottom; //of block + TO_ROW *row; //current row + TO_ROW_IT row_it = block->get_rows (); + BLOBNBOX_IT blob_it = &block->blobs; + + if (row_it.length () == 0) + return; //empty block + block_box = deskew_block_coords (block, gradient); + xleft = block->block->bounding_box ().left (); + ybottom = block->block->bounding_box ().bottom (); + min_y = block_box.bottom () - 1; + max_y = block_box.top () + 1; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + line_index = (INT32) floor (row_it.data ()->intercept ()); + if (line_index <= min_y) + min_y = line_index - 1; + if (line_index >= max_y) + max_y = line_index + 1; + } + line_count = max_y - min_y + 1; + if (line_count <= 0) + return; //empty block + deltas = (INT32 *) alloc_mem (line_count * sizeof (INT32)); + occupation = (INT32 *) alloc_mem (line_count * sizeof (INT32)); + if (deltas == NULL || occupation == NULL) + MEMORY_OUT.error ("compute_line_spacing", ABORT, NULL); + + compute_line_occupation(block, gradient, min_y, max_y, occupation, deltas); + compute_occupation_threshold ((INT32) + ceil (block->line_spacing * + (textord_merge_desc + + textord_merge_asc)), + (INT32) ceil (block->line_spacing * + (textord_merge_x + + textord_merge_asc)), + max_y - min_y + 1, occupation, deltas); +#ifndef GRAPHICS_DISABLED + if (testing_on) { + draw_occupation(xleft, ybottom, min_y, max_y, occupation, deltas); + } +#endif + compute_dropout_distances(occupation, deltas, line_count); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + line_index = (INT32) floor (row->intercept ()); + distance = deltas[line_index - min_y]; + if (find_best_dropout_row (row, distance, block->line_spacing / 2, + line_index, &row_it, testing_on)) { +#ifndef GRAPHICS_DISABLED + if (testing_on) + plot_parallel_row(row, gradient, block_edge, WHITE, rotation); +#endif + blob_it.add_list_after (row_it.data ()->blob_list ()); + delete row_it.extract (); //too far away + } + } + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + blob_it.add_list_after (row_it.data ()->blob_list ()); + } + + free_mem(deltas); + free_mem(occupation); +} + + +/********************************************************************** + * find_best_dropout_row + * + * Delete this row if it has a neighbour with better dropout characteristics. + * TRUE is returned if the row should be deleted. + **********************************************************************/ + +BOOL8 find_best_dropout_row( //find neighbours + TO_ROW *row, //row to test + INT32 distance, //dropout dist + float dist_limit, //threshold distance + INT32 line_index, //index of row + TO_ROW_IT *row_it, //current position + BOOL8 testing_on //correct orientation + ) { + INT32 next_index; //of neigbouring row + INT32 row_offset; //from current row + INT32 abs_dist; //absolute distance + INT8 row_inc; //increment to row_index + TO_ROW *next_row; //nextious row + + if (testing_on) + tprintf ("Row at %g(%g), dropout dist=%d,", + row->intercept (), row->parallel_c (), distance); + if (distance < 0) { + row_inc = 1; + abs_dist = -distance; + } + else { + row_inc = -1; + abs_dist = distance; + } + if (abs_dist > dist_limit) { + if (testing_on) { + tprintf (" too far - deleting\n"); + } + return TRUE; + } + if (distance < 0 && !row_it->at_last () + || distance >= 0 && !row_it->at_first ()) { + row_offset = row_inc; + do { + next_row = row_it->data_relative (row_offset); + next_index = (INT32) floor (next_row->intercept ()); + if (distance < 0 + && next_index < line_index + && next_index > line_index + distance + distance + || distance >= 0 + && next_index > line_index + && next_index < line_index + distance + distance) { + if (testing_on) { + tprintf (" nearer neighbour (%d) at %g\n", + line_index + distance - next_index, + next_row->intercept ()); + } + return TRUE; //other is nearer + } + else if (next_index == line_index + || next_index == line_index + distance + distance) { + if (row->believability () <= next_row->believability ()) { + if (testing_on) { + tprintf (" equal but more believable at %g (%g/%g)\n", + next_row->intercept (), + row->believability (), + next_row->believability ()); + } + return TRUE; //other is more believable + } + } + row_offset += row_inc; + } + while ((next_index == line_index + || next_index == line_index + distance + distance) + && row_offset < row_it->length ()); + if (testing_on) + tprintf (" keeping\n"); + } + return FALSE; +} + + +/********************************************************************** + * deskew_block_coords + * + * Compute the bounding box of all the blobs in the block + * if they were deskewed without actually doing it. + **********************************************************************/ + +BOX deskew_block_coords( //block box + TO_BLOCK *block, //block to do + float gradient //global skew + ) { + BOX result; //block bounds + BOX blob_box; //of block + FCOORD rotation; //deskew vector + float length; //of gradient vector + TO_ROW_IT row_it = block->get_rows (); + TO_ROW *row; //current row + BLOBNBOX *blob; //current blob + BLOBNBOX_IT blob_it; //iterator + + length = sqrt (gradient * gradient + 1); + rotation = FCOORD (1 / length, -gradient / length); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + blob_it.set_to_list (row->blob_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_box.rotate (rotation);//de-skew it + result += blob_box; + } + } + return result; +} + + +/********************************************************************** + * compute_line_occupation + * + * Compute the pixel projection back on the y axis given the global + * skew. Also compute the 1st derivative. + **********************************************************************/ + +void compute_line_occupation( //project blobs + TO_BLOCK *block, //block to do + float gradient, //global skew + INT32 min_y, //min coord in block + INT32 max_y, //in block + INT32 *occupation, //output projection + INT32 *deltas //derivative + ) { + INT32 line_count; //maxy-miny+1 + INT32 line_index; //of scan line + float top, bottom; //coords of blob + INT32 width; //of blob + TO_ROW *row; //current row + TO_ROW_IT row_it = block->get_rows (); + BLOBNBOX *blob; //current blob + BLOBNBOX_IT blob_it; //iterator + float length; //of skew vector + BOX blob_box; //bounding box + FCOORD rotation; //inverse of skew + + line_count = max_y - min_y + 1; + length = sqrt (gradient * gradient + 1); + rotation = FCOORD (1 / length, -gradient / length); + for (line_index = 0; line_index < line_count; line_index++) + deltas[line_index] = 0; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + blob_it.set_to_list (row->blob_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_box.rotate (rotation);//de-skew it + top = blob_box.top (); + bottom = blob_box.bottom (); + width = + (INT32) floor ((FLOAT32) (blob_box.right () - blob_box.left ())); + if ((INT32) floor (bottom) < min_y + || (INT32) floor (bottom) - min_y >= line_count) + fprintf (stderr, + "Bad y coord of bottom, " INT32FORMAT "(" INT32FORMAT "," + INT32FORMAT ")\n", (INT32) floor (bottom), min_y, max_y); + //count transitions + deltas[(INT32) floor (bottom) - min_y] += width; + if ((INT32) floor (top) < min_y + || (INT32) floor (top) - min_y >= line_count) + fprintf (stderr, + "Bad y coord of top, " INT32FORMAT "(" INT32FORMAT "," + INT32FORMAT ")\n", (INT32) floor (top), min_y, max_y); + deltas[(INT32) floor (top) - min_y] -= width; + } + } + occupation[0] = deltas[0]; + for (line_index = 1; line_index < line_count; line_index++) + occupation[line_index] = occupation[line_index - 1] + deltas[line_index]; +} + + +/********************************************************************** + * compute_occupation_threshold + * + * Compute thresholds for textline or not for the occupation array. + **********************************************************************/ + +void compute_occupation_threshold( //project blobs + INT32 low_window, //below result point + INT32 high_window, //above result point + INT32 line_count, //array sizes + INT32 *occupation, //input projection + INT32 *thresholds //output thresholds + ) { + INT32 line_index; //of thresholds line + INT32 low_index; //in occupation + INT32 high_index; //in occupation + INT32 sum; //current average + INT32 divisor; //to get thresholds + INT32 min_index; //of min occ + INT32 min_occ; //min in locality + INT32 test_index; //for finding min + + divisor = + (INT32) ceil ((low_window + high_window) / textord_occupancy_threshold); + if (low_window + high_window < line_count) { + for (sum = 0, high_index = 0; high_index < low_window; high_index++) + sum += occupation[high_index]; + for (low_index = 0; low_index < high_window; low_index++, high_index++) + sum += occupation[high_index]; + min_occ = occupation[0]; + min_index = 0; + for (test_index = 1; test_index < high_index; test_index++) { + if (occupation[test_index] <= min_occ) { + min_occ = occupation[test_index]; + min_index = test_index; //find min in region + } + } + for (line_index = 0; line_index < low_window; line_index++) + thresholds[line_index] = (sum - min_occ) / divisor + min_occ; + //same out to end + for (low_index = 0; high_index < line_count; low_index++, high_index++) { + sum -= occupation[low_index]; + sum += occupation[high_index]; + if (occupation[high_index] <= min_occ) { + //find min in region + min_occ = occupation[high_index]; + min_index = high_index; + } + //lost min from region + if (min_index <= low_index) { + min_occ = occupation[low_index + 1]; + min_index = low_index + 1; + for (test_index = low_index + 2; test_index <= high_index; + test_index++) { + if (occupation[test_index] <= min_occ) { + min_occ = occupation[test_index]; + //find min in region + min_index = test_index; + } + } + } + thresholds[line_index++] = (sum - min_occ) / divisor + min_occ; + } + } + else { + min_occ = occupation[0]; + min_index = 0; + for (sum = 0, low_index = 0; low_index < line_count; low_index++) { + if (occupation[low_index] < min_occ) { + min_occ = occupation[low_index]; + min_index = low_index; + } + sum += occupation[low_index]; + } + line_index = 0; + } + for (; line_index < line_count; line_index++) + thresholds[line_index] = (sum - min_occ) / divisor + min_occ; + //same out to end +} + + +/********************************************************************** + * compute_dropout_distances + * + * Compute the distance from each coordinate to the nearest dropout. + **********************************************************************/ + +void compute_dropout_distances( //project blobs + INT32 *occupation, //input projection + INT32 *thresholds, //output thresholds + INT32 line_count //array sizes + ) { + INT32 line_index; //of thresholds line + INT32 distance; //from prev dropout + INT32 next_dist; //to next dropout + INT32 back_index; //for back filling + INT32 prev_threshold; //before overwrite + + distance = -line_count; + line_index = 0; + do { + do { + distance--; + prev_threshold = thresholds[line_index]; + //distance from prev + thresholds[line_index] = distance; + line_index++; + } + while (line_index < line_count + && (occupation[line_index] < thresholds[line_index] + || occupation[line_index - 1] >= prev_threshold)); + if (line_index < line_count) { + back_index = line_index - 1; + next_dist = 1; + while (next_dist < -distance && back_index >= 0) { + thresholds[back_index] = next_dist; + back_index--; + next_dist++; + distance++; + } + distance = 1; + } + } + while (line_index < line_count); +} + + +/********************************************************************** + * expand_rows + * + * Expand each row to the least of its allowed size and touching its + * neighbours. If the expansion would entirely swallow a neighbouring row + * then do so. + **********************************************************************/ + +void expand_rows( //find lines + ICOORD page_tr, //top right + TO_BLOCK *block, //block to do + float gradient, //gradient to fit + FCOORD rotation, //for drawing + INT32 block_edge, //edge of block + BOOL8 testing_on //correct orientation + ) { + BOOL8 swallowed_row; //eaten a neighbour + float y_max, y_min; //new row limits + float y_bottom, y_top; //allowed limits + TO_ROW *test_row; //next row + TO_ROW *row; //current row + //iterators + BLOBNBOX_IT blob_it = &block->blobs; + TO_ROW_IT row_it = block->get_rows (); + +#ifndef GRAPHICS_DISABLED + if (textord_show_expanded_rows && testing_on) { + if (to_win == NO_WINDOW) + create_to_win(page_tr); + } +#endif + + adjust_row_limits(block); //shift min,max. + if (textord_new_initial_xheight) { + if (block->get_rows ()->length () == 0) + return; + compute_row_stats(block, textord_show_expanded_rows &&testing_on); + } + assign_blobs_to_rows (block, &gradient, 4, TRUE, FALSE, FALSE); + //get real membership + if (block->get_rows ()->length () == 0) + return; + fit_parallel_rows(block, + gradient, + rotation, + block_edge, + textord_show_expanded_rows &&testing_on); + if (!textord_new_initial_xheight) + compute_row_stats(block, textord_show_expanded_rows &&testing_on); + row_it.move_to_last (); + do { + row = row_it.data (); + y_max = row->max_y (); //get current limits + y_min = row->min_y (); + y_bottom = row->intercept () - block->line_size * textord_merge_desc; + y_top = row->intercept () + block->line_size + * (textord_merge_x + textord_merge_asc); + if (y_min > y_bottom) { //expansion allowed + //expandable + swallowed_row = TRUE; + while (swallowed_row && !row_it.at_last ()) { + swallowed_row = FALSE; + //get next one + test_row = row_it.data_relative (1); + //overlaps space + if (test_row->max_y () > y_bottom) { + if (test_row->min_y () > y_bottom) { + row_it.forward (); +#ifndef GRAPHICS_DISABLED + if (textord_show_expanded_rows && testing_on) + plot_parallel_row(test_row, + gradient, + block_edge, + WHITE, + rotation); +#endif + blob_it.set_to_list (row->blob_list ()); + blob_it.add_list_after (test_row->blob_list ()); + //swallow complete row + delete row_it.extract (); + row_it.backward (); + swallowed_row = TRUE; + } + else if (test_row->max_y () < y_min) + //shorter limit + y_bottom = test_row->max_y (); + else + y_bottom = y_min; //can't expand it + } + } + y_min = y_bottom; //expand it + } + if (y_max < y_top) { //expansion allowed + swallowed_row = TRUE; + while (swallowed_row && !row_it.at_first ()) { + swallowed_row = FALSE; + //get one above + test_row = row_it.data_relative (-1); + if (test_row->min_y () < y_top) { + if (test_row->max_y () < y_top) { + row_it.backward (); + blob_it.set_to_list (row->blob_list ()); +#ifndef GRAPHICS_DISABLED + if (textord_show_expanded_rows && testing_on) + plot_parallel_row(test_row, + gradient, + block_edge, + WHITE, + rotation); +#endif + blob_it.add_list_after (test_row->blob_list ()); + //swallow complete row + delete row_it.extract (); + row_it.forward (); + swallowed_row = TRUE; + } + else if (test_row->min_y () < y_max) + //shorter limit + y_top = test_row->min_y (); + else + y_top = y_max; //can't expand it + + } + } + y_max = y_top; + } + //new limits + row->set_limits (y_min, y_max); + row_it.backward (); + } + while (!row_it.at_last ()); +} + + +/********************************************************************** + * adjust_row_limits + * + * Change the limits of rows to suit the default fractions. + **********************************************************************/ + +void adjust_row_limits( //tidy limits + TO_BLOCK *block //block to do + ) { + TO_ROW *row; //current row + float size; //size of row + float ymax; //top of row + float ymin; //bottom of row + TO_ROW_IT row_it = block->get_rows (); + + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + size = row->max_y () - row->min_y (); + size /= textord_merge_x + textord_merge_asc + textord_merge_desc; + ymax = size * (textord_merge_x + textord_merge_asc); + ymin = -size * textord_merge_desc; + row->set_limits (row->intercept () + ymin, row->intercept () + ymax); + row->merged = FALSE; + } +} + + +/********************************************************************** + * compute_row_stats + * + * Compute the linespacing and offset. + **********************************************************************/ + +void compute_row_stats( //find lines + TO_BLOCK *block, //block to do + BOOL8 testing_on //correct orientation + ) { + INT32 row_index; //of median + TO_ROW *row; //current row + TO_ROW *prev_row; //previous row + float iqr; //inter quartile range + TO_ROW_IT row_it = block->get_rows (); + //number of rows + INT16 rowcount = row_it.length (); + TO_ROW **rows; //for choose nth + + rows = (TO_ROW **) alloc_mem (rowcount * sizeof (TO_ROW *)); + if (rows == NULL) + MEMORY_OUT.error ("compute_row_stats", ABORT, NULL); + rowcount = 0; + prev_row = NULL; + row_it.move_to_last (); //start at bottom + do { + row = row_it.data (); + if (prev_row != NULL) { + rows[rowcount++] = prev_row; + prev_row->spacing = row->intercept () - prev_row->intercept (); + if (testing_on) + tprintf ("Row at %g yields spacing of %g\n", + row->intercept (), prev_row->spacing); + } + prev_row = row; + row_it.backward (); + } + while (!row_it.at_last ()); + block->key_row = prev_row; + block->baseline_offset = + fmod (prev_row->parallel_c (), block->line_spacing); + if (testing_on) + tprintf ("Blob based spacing=(%g,%g), offset=%g", + block->line_size, block->line_spacing, block->baseline_offset); + if (rowcount > 0) { + row_index = choose_nth_item (rowcount * 3 / 4, rows, rowcount, + sizeof (TO_ROW *), row_spacing_order); + iqr = rows[row_index]->spacing; + row_index = choose_nth_item (rowcount / 4, rows, rowcount, + sizeof (TO_ROW *), row_spacing_order); + iqr -= rows[row_index]->spacing; + row_index = choose_nth_item (rowcount / 2, rows, rowcount, + sizeof (TO_ROW *), row_spacing_order); + block->key_row = rows[row_index]; + if (testing_on) + tprintf (" row based=%g(%g)", rows[row_index]->spacing, iqr); + if (rowcount > 2 + && iqr < rows[row_index]->spacing * textord_linespace_iqrlimit) { + if (!textord_new_initial_xheight) { + if (rows[row_index]->spacing < block->line_spacing + && rows[row_index]->spacing > block->line_size) + //within range + block->line_size = rows[row_index]->spacing; + //spacing=size + else if (rows[row_index]->spacing > block->line_spacing) + block->line_size = block->line_spacing; + //too big so use max + } + else { + if (rows[row_index]->spacing < block->line_spacing) + block->line_size = rows[row_index]->spacing; + else + block->line_size = block->line_spacing; + //too big so use max + } + if (block->line_size < textord_min_xheight) + block->line_size = (float) textord_min_xheight; + block->line_spacing = rows[row_index]->spacing; + block->max_blob_size = + block->line_spacing * textord_excess_blobsize; + } + block->baseline_offset = fmod (rows[row_index]->intercept (), + block->line_spacing); + } + if (testing_on) + tprintf ("\nEstimate line size=%g, spacing=%g, offset=%g\n", + block->line_size, block->line_spacing, block->baseline_offset); + free_mem(rows); +} + + +/********************************************************************** + * compute_block_xheight + * + * Compute the xheight of the individual rows, then correlate them + * and interpret ascenderless lines, correcting xheights. + **********************************************************************/ + +void compute_block_xheight( //find lines + TO_BLOCK *block, //block to do + float gradient //global skew + ) { + TO_ROW *row; //current row + int xh_count, desc_count; //no of samples + float block_median; //median blob size + int asc_count, cap_count; + INT32 min_size, max_size; //limits on xheight + INT32 evidence; //no of samples on row + float xh_sum, desc_sum; //for averages + float asc_sum, cap_sum; + TO_ROW_IT row_it = block->get_rows (); + STATS row_heights; //block evidence + + if (row_it.empty ()) + return; //no rows + block_median = median_block_xheight (block, gradient); + block_median *= 2; + if (block_median < block->line_size) + block_median = block->line_size; + // tprintf("Block median=%g, linesize=%g\n", + // block_median,block->line_size); + max_size = (INT32) ceil (block_median); + min_size = (INT32) floor (block_median * textord_minxh); + row_heights.set_range (min_size, max_size + 1); + xh_count = desc_count = asc_count = cap_count = 0; + xh_sum = desc_sum = asc_sum = cap_sum = 0.0f; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + evidence = compute_row_xheight (row, min_size, max_size, gradient); + if (row->xheight > 0 && row->ascrise > 0) { + row_heights.add ((INT32) row->xheight, evidence); + xh_count += evidence; + asc_sum += row->ascrise; + asc_count++; + } + else if (row->xheight > 0) { + cap_sum += row->xheight; //assume just caps + cap_count++; + } + if (row->descdrop != 0) { + desc_sum += row->descdrop; + desc_count++; + } + } + if (xh_count > 0) { + //median + xh_sum = row_heights.ile (0.5); + asc_sum /= asc_count; + } + else if (cap_count > 0) { + cap_sum /= cap_count; //must assume caps + xh_sum = + cap_sum * textord_merge_x / (textord_merge_x + textord_merge_asc); + asc_sum = + cap_sum * textord_merge_asc / (textord_merge_x + textord_merge_asc); + } + else { + //default sizes + xh_sum = block_median * textord_merge_x; + asc_sum = block_median * textord_merge_asc; + } + if (desc_count > 0) { + desc_sum /= desc_count; + } + else { + desc_sum = xh_sum * textord_merge_desc / textord_merge_x; + } + // tprintf("Block average x height=%g, count=%d, asc=%g/%d, desc=%g/%d,cap=%g/%d\n", + // xh_sum,xh_count,asc_sum,asc_count,desc_sum,desc_count, + // cap_sum,cap_count); + if (xh_sum < textord_min_xheight) + xh_sum = (float) textord_min_xheight; + block->xheight = xh_sum; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + correct_row_xheight (row_it.data (), xh_sum, asc_sum, desc_sum); + } +} + + +/********************************************************************** + * median_block_xheight + * + * Compute the linespacing and offset. + **********************************************************************/ + +float median_block_xheight( //find lines + TO_BLOCK *block, //block to do + float gradient //global skew + ) { + TO_ROW *row; //current row + float result; //output size + float xcentre; //centre of blob + TO_ROW_IT row_it = block->get_rows (); + BLOBNBOX_IT blob_it; + BLOBNBOX *blob; //current blob + float *heights; //for choose nth + INT32 blob_count; //blobs in block + INT32 blob_index; //current blob + + blob_count = 0; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) + blob_count += row_it.data ()->blob_list ()->length (); + heights = (float *) alloc_mem (blob_count * sizeof (float)); + if (heights == NULL) + MEMORY_OUT.error ("compute_row_stats", ABORT, NULL); + + blob_index = 0; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + blob_it.set_to_list (row->blob_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + if (!blob->joined_to_prev ()) { + xcentre = + (blob->bounding_box ().left () + + blob->bounding_box ().right ()) / 2.0f; + heights[blob_index] = + blob->bounding_box ().top () - gradient * xcentre - + row->parallel_c (); + if (heights[blob_index] > 0) + blob_index++; + } + } + } + ASSERT_HOST (blob_index > 0); //dont expect 0 + blob_count = blob_index; + blob_index = choose_nth_item (blob_count / 2, heights, blob_count); + result = heights[blob_index]; + free_mem(heights); + return result; +} + + +/********************************************************************** + * compute_row_xheight + * + * Estimate the xheight of this row. + * Compute the ascender rise and descender drop at the same time. + **********************************************************************/ + +INT32 compute_row_xheight( //find lines + TO_ROW *row, //row to do + INT32 min_height, //min xheight + INT32 max_height, //max xheight + float gradient //global skew + ) { + BOOL8 in_best_pile; //control of mode size + INT32 prev_size; //previous size + float xcentre; //centre of blob + float height; //height of blob + BLOBNBOX_IT blob_it = row->blob_list (); + BLOBNBOX *blob; //current blob + INT32 blob_count; //blobs in block + INT32 x; //xheight index + INT32 asc; //ascender index + INT32 blob_index; //current blob + INT32 mode_count; //no of modes + INT32 best_count; //count of best x so far + float ratio; //size ratio + INT32 modes[MAX_HEIGHT_MODES]; //biggest piles + STATS heights (min_height, max_height + 1); + + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (!blob->joined_to_prev ()) { + xcentre = + (blob->bounding_box ().left () + + blob->bounding_box ().right ()) / 2.0f; + height = blob->bounding_box ().top (); + if (textord_fix_xheight_bug) + height -= row->baseline.y (xcentre); + else + height -= gradient * xcentre + row->parallel_c (); + if (height >= min_height && height <= max_height + && (!textord_xheight_tweak || height > textord_min_xheight)) + heights.add ((INT32) floor (height + 0.5), 1); + } + } + blob_index = heights.mode (); //find mode + //get count of mode + blob_count = heights.pile_count (blob_index); + if (textord_debug_xheights) + tprintf ("min_height=%d, max_height=%d, mode=%d, count=%d, total=%d,%d\n", + min_height, max_height, blob_index, blob_count, + heights.get_total (), row->blob_list ()->length ()); + row->ascrise = 0.0f; + row->xheight = 0.0f; + row->descdrop = 0.0f; //undefined; + in_best_pile = FALSE; + prev_size = -MAX_INT32; + best_count = 0; + if (blob_count > 0) { + //get biggest ones + mode_count = compute_height_modes (&heights, min_height, max_height, modes, MAX_HEIGHT_MODES); + for (x = 0; x < mode_count - 1; x++) { + if (modes[x] != prev_size + 1) + in_best_pile = FALSE; //had empty height + if (heights.pile_count (modes[x]) + >= blob_count * textord_xheight_mode_fraction + && (in_best_pile || heights.pile_count (modes[x]) > best_count)) { + for (asc = x + 1; asc < mode_count; asc++) { + ratio = (float) modes[asc] / modes[x]; + if (textord_ascx_ratio_min < ratio + && ratio < textord_ascx_ratio_max + && heights.pile_count (modes[asc]) + >= blob_count * textord_ascheight_mode_fraction) { + if (heights.pile_count (modes[x]) > best_count) { + in_best_pile = TRUE; + best_count = heights.pile_count (modes[x]); + } + // tprintf("X=%d, asc=%d, count=%d, ratio=%g\n", + // modes[x],modes[asc]-modes[x], + // heights.pile_count(modes[x]), + // ratio); + prev_size = modes[x]; + row->xheight = (float) modes[x]; + row->ascrise = (float) (modes[asc] - modes[x]); + } + } + } + } + if (row->xheight == 0) { + //single mode + row->xheight = (float) blob_index; + row->ascrise = 0.0f; + if (textord_debug_xheights) + tprintf ("Single mode xheight set to %g\n", row->xheight); + } + else if (textord_debug_xheights) + tprintf ("Multi-mode xheight set to %g, asc=%g\n", + row->xheight, row->ascrise); + row->descdrop = (float) compute_row_descdrop (row, gradient); + //find descenders + } + return best_count; +} + + +/********************************************************************** + * compute_row_descdrop + * + * Estimate the descdrop of this row. + **********************************************************************/ + +INT32 compute_row_descdrop( //find lines + TO_ROW *row, //row to do + float gradient //global skew + ) { + INT32 min_height = (INT32) floor (row->xheight * textord_descx_ratio_min); + INT32 max_height = (INT32) floor (row->xheight * textord_descx_ratio_max); + float xcentre; //centre of blob + float height; //height of blob + BLOBNBOX_IT blob_it = row->blob_list (); + BLOBNBOX *blob; //current blob + INT32 blob_count; //blobs in block + INT32 blob_index; //current blob + STATS heights (min_height, max_height + 1); + + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (!blob->joined_to_prev ()) { + xcentre = + (blob->bounding_box ().left () + + blob->bounding_box ().right ()) / 2.0f; + height = + gradient * xcentre + row->parallel_c () - + blob->bounding_box ().bottom (); + if (height >= min_height && height <= max_height) + heights.add ((INT32) floor (height + 0.5), 1); + } + } + blob_index = heights.mode (); //find mode + //get count of mode + blob_count = heights.pile_count (blob_index); + return blob_count > 0 ? -blob_index : 0; +} + + +/********************************************************************** + * compute_height_modes + * + * Find the top maxmodes values in the input array and put their + * indices in the output in the order in which they occurred. + **********************************************************************/ + +INT32 compute_height_modes( //find lines + STATS *heights, //stats to search + INT32 min_height, //bottom of range + INT32 max_height, //top of range + INT32 *modes, //output array + INT32 maxmodes //size of modes + ) { + INT32 pile_count; //no in source pile + INT32 src_count; //no of source entries + INT32 src_index; //current entry + INT32 least_count; //height of smalllest + INT32 least_index; //index of least + INT32 dest_count; //index in modes + + src_count = max_height + 1 - min_height; + dest_count = 0; + least_count = MAX_INT32; + least_index = -1; + for (src_index = 0; src_index < src_count; src_index++) { + pile_count = heights->pile_count (min_height + src_index); + if (pile_count > 0) { + if (dest_count < maxmodes) { + if (pile_count < least_count) { + //find smallest in array + least_count = pile_count; + least_index = dest_count; + } + modes[dest_count++] = min_height + src_index; + } + else if (pile_count >= least_count) { + while (least_index < maxmodes - 1) { + modes[least_index] = modes[least_index + 1]; + //shuffle up + least_index++; + } + //new one on end + modes[maxmodes - 1] = min_height + src_index; + if (pile_count == least_count) { + //new smallest + least_index = maxmodes - 1; + } + else { + least_count = heights->pile_count (modes[0]); + least_index = 0; + for (dest_count = 1; dest_count < maxmodes; dest_count++) { + pile_count = heights->pile_count (modes[dest_count]); + if (pile_count < least_count) { + //find smallest + least_count = pile_count; + least_index = dest_count; + } + } + } + } + } + } + return dest_count; +} + + +/********************************************************************** + * correct_row_xheight + * + * Adjust the xheight etc of this row if not within reasonable limits + * of the average for the block. + **********************************************************************/ + +void correct_row_xheight( //fix bad values + TO_ROW *row, //row to fix + float xheight, //average values + float ascrise, + float descdrop) { + if (textord_row_xheights) { + if (row->xheight <= 0) + row->xheight = xheight; + if (row->ascrise < row->xheight * (textord_ascx_ratio_min - 1)) { + if (row->xheight >= xheight * (1 - textord_xheight_error_margin) + && row->xheight <= xheight * (1 + textord_xheight_error_margin)) { + row->all_caps = FALSE; + row->ascrise = ascrise; + } + else if (row->xheight >= + (xheight + ascrise) * (1 - textord_xheight_error_margin) + && row->xheight <= + (xheight + ascrise) * (1 + textord_xheight_error_margin)) { + row->all_caps = TRUE; + //it was caps + row->ascrise = row->xheight - xheight; + row->xheight = xheight; + } + else { + row->all_caps = TRUE; + row->ascrise = row->xheight * ascrise / (xheight + ascrise); + row->xheight -= row->ascrise; + } + } + else + row->all_caps = FALSE; + row->ascrise = ascrise; + if (row->descdrop >= -row->xheight * (textord_ascx_ratio_min - 1)) + row->descdrop = descdrop; + } + else { + if (row->xheight < xheight * (1 - textord_xheight_error_margin) + || row->xheight > xheight * (1 + textord_xheight_error_margin)) + row->xheight = xheight; //set to average + row->all_caps = row->ascrise <= 0; + if (row->ascrise < ascrise * (1 - textord_xheight_error_margin) + || row->ascrise > ascrise * (1 + textord_xheight_error_margin)) + row->ascrise = ascrise; //set to average + if (row->descdrop < descdrop * (1 - textord_xheight_error_margin) + || row->descdrop > descdrop * (1 + textord_xheight_error_margin)) + row->descdrop = descdrop; //set to average + } +} + + +/********************************************************************** + * separate_underlines + * + * Test wide objects for being potential underlines. If they are then + * put them in a separate list in the block. + **********************************************************************/ + +void separate_underlines( //make rough chars + TO_BLOCK *block, //block to do + float gradient, //skew angle + FCOORD rotation, //inverse landscape + BOOL8 testing_on //correct orientation + ) { + BLOBNBOX *blob; //current blob + PBLOB *poly_blob; //rotated blob + C_BLOB *rotated_blob; //rotated blob + TO_ROW *row; //current row + float length; //of g_vec + BOX blob_box; + FCOORD blob_rotation; //inverse of rotation + FCOORD g_vec; //skew rotation + BLOBNBOX_IT blob_it; //iterator + //iterator + BLOBNBOX_IT under_it = &block->underlines; + TO_ROW_IT row_it = block->get_rows (); + + //length of vector + length = sqrt (1 + gradient * gradient); + g_vec = FCOORD (1 / length, -gradient / length); + blob_rotation = FCOORD (rotation.x (), -rotation.y ()); + blob_rotation.rotate (g_vec); //unoding everything + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + //get blobs + blob_it.set_to_list (row->blob_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + if (blob_box.width () > block->line_size * textord_underline_width) { + if (textord_cblob_blockocc && blob->cblob () != NULL) { + rotated_blob = crotate_cblob (blob->cblob (), + blob_rotation); + if (test_underline (testing_on && textord_show_final_rows, + rotated_blob, (INT16) row->intercept (), + (INT16) (block->line_size * + (textord_merge_x + + textord_merge_asc / 2.0f)))) { + under_it.add_after_then_move (blob_it.extract ()); + if (testing_on && textord_show_final_rows) { + tprintf ("Underlined blob at (%d,%d)->(%d,%d) ", + rotated_blob->bounding_box ().left (), + rotated_blob->bounding_box ().bottom (), + rotated_blob->bounding_box ().right (), + rotated_blob->bounding_box ().top ()); + tprintf ("(Was (%d,%d)->(%d,%d))\n", + blob_box.left (), blob_box.bottom (), + blob_box.right (), blob_box.top ()); + } + } + delete rotated_blob; + } + else { + if (blob->blob () != NULL) { + // if (testing_on && textord_show_final_rows) + // tprintf("Rotating by (%g,%g)\n", + // blob_rotation.x(),blob_rotation.y()); + poly_blob = rotate_blob (blob->blob (), blob_rotation); + } + else + poly_blob = rotate_cblob (blob->cblob (), + block->line_size, + blob_rotation); + if (test_underline + (testing_on + && textord_show_final_rows, poly_blob, + row->intercept (), + block->line_size * (textord_merge_x + + textord_merge_asc / 2))) { + if (testing_on && textord_show_final_rows) { + tprintf ("Underlined blob at (%d,%d)->(%d,%d) ", + poly_blob->bounding_box ().left (), + poly_blob->bounding_box ().bottom (), + poly_blob->bounding_box ().right (), + poly_blob->bounding_box ().top ()); + tprintf ("(Was (%d,%d)->(%d,%d))\n", + blob_box.left (), blob_box.bottom (), + blob_box.right (), blob_box.top ()); + } + under_it.add_after_then_move (blob_it.extract ()); + } + delete poly_blob; + } + } + } + } +} + + +/********************************************************************** + * pre_associate_blobs + * + * Associate overlapping blobs and fake chop wide blobs. + **********************************************************************/ + +void pre_associate_blobs( //make rough chars + ICOORD page_tr, //top right + TO_BLOCK *block, //block to do + FCOORD rotation, //inverse landscape + BOOL8 testing_on //correct orientation + ) { +#ifndef GRAPHICS_DISABLED + COLOUR colour; //of boxes +#endif + INT16 overlap; //of adjacent boxes + BLOBNBOX *blob; //current blob + BLOBNBOX *nextblob; //next in list + BOX blob_box; + BOX next_box; //next blob + FCOORD blob_rotation; //inverse of rotation + BLOBNBOX_IT blob_it; //iterator + BLOBNBOX_IT start_it; //iterator + TO_ROW_IT row_it = block->get_rows (); + +#ifndef GRAPHICS_DISABLED + colour = RED; +#endif + + blob_rotation = FCOORD (rotation.x (), -rotation.y ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + //get blobs + blob_it.set_to_list (row_it.data ()->blob_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + start_it = blob_it; //save start point + // if (testing_on && textord_show_final_blobs) + // { + // tprintf("Blob at (%d,%d)->(%d,%d), addr=%x, count=%d\n", + // blob_box.left(),blob_box.bottom(), + // blob_box.right(),blob_box.top(), + // (void*)blob,blob_it.length()); + // } + do { + if (!blob_it.at_last ()) { + nextblob = blob_it.data_relative (1); + next_box = nextblob->bounding_box (); + overlap = next_box.width (); + if (blob_box.left () > next_box.left ()) + overlap -= blob_box.left () - next_box.left (); + if (blob_box.right () < next_box.right ()) + overlap -= next_box.right () - blob_box.right (); + if (overlap >= next_box.width () / 2 + || overlap >= blob_box.width () / 2) { + //merge new blob + blob->merge (nextblob); + //get bigger box + blob_box = blob->bounding_box (); + blob_it.forward (); + } + else + overlap = -1; //no overlap + } + else + overlap = -1; //no overlap + } + while (overlap >= 0); + blob->chop (&start_it, &blob_it, + blob_rotation, + block->line_size * textord_merge_x * + textord_chop_width); + //attempt chop + } +#ifndef GRAPHICS_DISABLED + if (testing_on && textord_show_final_blobs) { + if (to_win == NO_WINDOW) + create_to_win(page_tr); + perimeter_color_index(to_win, colour); + interior_style(to_win, INT_HOLLOW, TRUE); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_box.rotate (rotation); + if (!blob->joined_to_prev ()) { + rectangle (to_win, blob_box.left (), blob_box.bottom (), + blob_box.right (), blob_box.top ()); + } + } + colour = (COLOUR) (colour + 1); + if (colour > MAGENTA) + colour = RED; + } +#endif + } +} + + +/********************************************************************** + * fit_parallel_rows + * + * Re-fit the rows in the block to the given gradient. + **********************************************************************/ + +void fit_parallel_rows( //find lines + TO_BLOCK *block, //block to do + float gradient, //gradient to fit + FCOORD rotation, //for drawing + INT32 block_edge, //edge of block + BOOL8 testing_on //correct orientation + ) { +#ifndef GRAPHICS_DISABLED + COLOUR colour; //of row +#endif + TO_ROW_IT row_it = block->get_rows (); + + row_it.move_to_first (); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + if (row_it.data ()->blob_list ()->empty ()) + delete row_it.extract (); //nothing in it + else + fit_parallel_lms (gradient, row_it.data ()); + } +#ifndef GRAPHICS_DISABLED + if (testing_on) { + colour = RED; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + plot_parallel_row (row_it.data (), gradient, + block_edge, colour, rotation); + colour = (COLOUR) (colour + 1); + if (colour > MAGENTA) + colour = RED; + } + } +#endif + row_it.sort (row_y_order); //may have gone out of order +} + + +/********************************************************************** + * fit_parallel_lms + * + * Fit an LMS line to a row. + * Make the fit parallel to the given gradient and set the + * row accordingly. + **********************************************************************/ + +void fit_parallel_lms( //sort function + float gradient, //forced gradient + TO_ROW *row //row to fit + ) { + float c; //fitted line + int blobcount; //no of blobs + BOX box; //blob box + LMS lms (row->blob_list ()->length ()); + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + + blobcount = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + if (!blob_it.data ()->joined_to_prev ()) { + box = blob_it.data ()->bounding_box (); + lms. + add (FCOORD ((box.left () + box.right ()) / 2.0, box.bottom ())); + blobcount++; + } + } + lms.constrained_fit (gradient, c); + row->set_parallel_line (gradient, c, lms.error ()); + if (textord_straight_baselines && blobcount > lms_line_trials) { + lms.fit (gradient, c); + } + //set the other too + row->set_line (gradient, c, lms.error ()); +} + + +/********************************************************************** + * make_spline_rows + * + * Re-fit the rows in the block to the given gradient. + **********************************************************************/ + +void make_spline_rows( //find lines + TO_BLOCK *block, //block to do + float gradient, //gradient to fit + FCOORD rotation, //for drawing + INT32 block_edge, //edge of block + BOOL8 testing_on //correct orientation + ) { +#ifndef GRAPHICS_DISABLED + COLOUR colour; //of row +#endif + TO_ROW_IT row_it = block->get_rows (); + + row_it.move_to_first (); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + if (row_it.data ()->blob_list ()->empty ()) + delete row_it.extract (); //nothing in it + else + make_baseline_spline (row_it.data (), block); + } + if (textord_old_baselines) { +#ifndef GRAPHICS_DISABLED + if (testing_on) { + colour = RED; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); + row_it.forward ()) { + row_it.data ()->baseline.plot (to_win, colour); + colour = (COLOUR) (colour + 1); + if (colour > MAGENTA) + colour = RED; + } + } +#endif + make_old_baselines(block, testing_on); + } +#ifndef GRAPHICS_DISABLED + if (testing_on) { + colour = RED; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row_it.data ()->baseline.plot (to_win, colour); + colour = (COLOUR) (colour + 1); + if (colour > MAGENTA) + colour = RED; + } + } +#endif +} + + +/********************************************************************** + * make_baseline_spline + * + * Fit an LMS line to a row. + * Make the fit parallel to the given gradient and set the + * row accordingly. + **********************************************************************/ + +void make_baseline_spline( //sort function + TO_ROW *row, //row to fit + TO_BLOCK *block //block it came from + ) { + float b, c; //fitted curve + float middle; //x middle of blob + BOX box; //blob box + LMS lms (row->blob_list ()->length ()); + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + INT32 *xstarts; //spline boundaries + double *coeffs; //quadratic coeffs + INT32 segments; //no of segments + INT32 segment; //current segment + + xstarts = + (INT32 *) alloc_mem ((row->blob_list ()->length () + 1) * sizeof (INT32)); + if (segment_baseline (row, block, segments, xstarts) + && !textord_straight_baselines && !textord_parallel_baselines) { + if (textord_quadratic_baselines) { + coeffs = (double *) alloc_mem (segments * 3 * sizeof (double)); + for (segment = 0; segment < segments; segment++) { + lms.clear (); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + if (!blob_it.data ()->joined_to_prev ()) { + box = blob_it.data ()->bounding_box (); + middle = (box.left () + box.right ()) / 2.0; + if (middle >= xstarts[segment] + && middle < xstarts[segment + 1]) { + lms.add (FCOORD (middle, box.bottom ())); + } + } + } + if (textord_quadratic_baselines) + lms.fit_quadratic (block->line_size * + textord_spline_outlier_fraction, + coeffs[segment * 3], b, c); + else { + lms.fit (b, c); + coeffs[segment * 3] = 0; + } + coeffs[segment * 3 + 1] = b; + coeffs[segment * 3 + 2] = c; + } + } + else + coeffs = linear_spline_baseline (row, block, segments, xstarts); + } + else { + xstarts[1] = xstarts[segments]; + segments = 1; + coeffs = (double *) alloc_mem (3 * sizeof (double)); + coeffs[0] = 0; + coeffs[1] = row->line_m (); + coeffs[2] = row->line_c (); + } + row->baseline = QSPLINE (segments, xstarts, coeffs); + free_mem(coeffs); + free_mem(xstarts); +} + + +/********************************************************************** + * segment_baseline + * + * Divide the baseline up into segments which require a different + * quadratic fitted to them. + * Return TRUE if enough blobs were far enough away to need a quadratic. + **********************************************************************/ + +BOOL8 +segment_baseline ( //split baseline +TO_ROW * row, //row to fit +TO_BLOCK * block, //block it came from +INT32 & segments, //no fo segments +INT32 xstarts[] //coords of segments +) { + BOOL8 needs_curve; //needs curved line + int blobcount; //no of blobs + int blobindex; //current blob + int last_state; //above, on , below + int state; //of current blob + float yshift; //from baseline + BOX box; //blob box + BOX new_box; //new_it box + float middle; //xcentre of blob + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + BLOBNBOX_IT new_it = blob_it; //front end + SORTED_FLOATS yshifts; //shifts from baseline + + needs_curve = FALSE; + box = box_next_pre_chopped (&blob_it); + xstarts[0] = box.left (); + segments = 1; + blobcount = row->blob_list ()->length (); + if (textord_oldbl_debug) + tprintf ("Segmenting baseline of %d blobs at (%d,%d)\n", + blobcount, box.left (), box.bottom ()); + if (blobcount <= textord_spline_medianwin + || blobcount < textord_spline_minblobs) { + blob_it.move_to_last (); + box = blob_it.data ()->bounding_box (); + xstarts[1] = box.right (); + return FALSE; + } + last_state = 0; + new_it.mark_cycle_pt (); + for (blobindex = 0; blobindex < textord_spline_medianwin; blobindex++) { + new_box = box_next_pre_chopped (&new_it); + middle = (new_box.left () + new_box.right ()) / 2.0; + yshift = new_box.bottom () - row->line_m () * middle - row->line_c (); + //record shift + yshifts.add (yshift, blobindex); + if (new_it.cycled_list ()) { + xstarts[1] = new_box.right (); + return FALSE; + } + } + for (blobcount = 0; blobcount < textord_spline_medianwin / 2; blobcount++) + box = box_next_pre_chopped (&blob_it); + do { + new_box = box_next_pre_chopped (&new_it); + //get middle one + yshift = yshifts[textord_spline_medianwin / 2]; + if (yshift > textord_spline_shift_fraction * block->line_size) + state = 1; + else if (-yshift > textord_spline_shift_fraction * block->line_size) + state = -1; + else + state = 0; + if (state != 0) + needs_curve = TRUE; + // tprintf("State=%d, prev=%d, shift=%g\n", + // state,last_state,yshift); + if (state != last_state && blobcount > textord_spline_minblobs) { + xstarts[segments++] = box.left (); + blobcount = 0; + } + last_state = state; + yshifts.remove (blobindex - textord_spline_medianwin); + box = box_next_pre_chopped (&blob_it); + middle = (new_box.left () + new_box.right ()) / 2.0; + yshift = new_box.bottom () - row->line_m () * middle - row->line_c (); + yshifts.add (yshift, blobindex); + blobindex++; + blobcount++; + } + while (!new_it.cycled_list ()); + if (blobcount > textord_spline_minblobs || segments == 1) { + xstarts[segments] = new_box.right (); + } + else { + xstarts[--segments] = new_box.right (); + } + if (textord_oldbl_debug) + tprintf ("Made %d segments on row at (%d,%d)\n", + segments, box.right (), box.bottom ()); + return needs_curve; +} + + +/********************************************************************** + * linear_spline_baseline + * + * Divide the baseline up into segments which require a different + * quadratic fitted to them. + * Return TRUE if enough blobs were far enough away to need a quadratic. + **********************************************************************/ + +double * +linear_spline_baseline ( //split baseline +TO_ROW * row, //row to fit +TO_BLOCK * block, //block it came from +INT32 & segments, //no fo segments +INT32 xstarts[] //coords of segments +) { + int blobcount; //no of blobs + int blobindex; //current blob + int index1, index2; //blob numbers + int blobs_per_segment; //blobs in each + BOX box; //blob box + BOX new_box; //new_it box + float middle; //xcentre of blob + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + BLOBNBOX_IT new_it = blob_it; //front end + float b, c; //fitted curve + LMS lms (row->blob_list ()->length ()); + double *coeffs; //quadratic coeffs + INT32 segment; //current segment + + box = box_next_pre_chopped (&blob_it); + xstarts[0] = box.left (); + blobcount = 1; + while (!blob_it.at_first ()) { + blobcount++; + box = box_next_pre_chopped (&blob_it); + } + segments = blobcount / textord_spline_medianwin; + if (segments < 1) + segments = 1; + blobs_per_segment = blobcount / segments; + coeffs = (double *) alloc_mem (segments * 3 * sizeof (double)); + if (textord_oldbl_debug) + tprintf + ("Linear splining baseline of %d blobs at (%d,%d), into %d segments of %d blobs\n", + blobcount, box.left (), box.bottom (), segments, blobs_per_segment); + segment = 1; + for (index2 = 0; index2 < blobs_per_segment / 2; index2++) + box_next_pre_chopped(&new_it); + index1 = 0; + blobindex = index2; + do { + blobindex += blobs_per_segment; + lms.clear (); + while (index1 < blobindex || segment == segments && index1 < blobcount) { + box = box_next_pre_chopped (&blob_it); + middle = (box.left () + box.right ()) / 2.0; + lms.add (FCOORD (middle, box.bottom ())); + index1++; + if (index1 == blobindex - blobs_per_segment / 2 + || index1 == blobcount - 1) { + xstarts[segment] = box.left (); + } + } + lms.fit (b, c); + coeffs[segment * 3 - 3] = 0; + coeffs[segment * 3 - 2] = b; + coeffs[segment * 3 - 1] = c; + segment++; + if (segment > segments) + break; + + blobindex += blobs_per_segment; + lms.clear (); + while (index2 < blobindex || segment == segments && index2 < blobcount) { + new_box = box_next_pre_chopped (&new_it); + middle = (new_box.left () + new_box.right ()) / 2.0; + lms.add (FCOORD (middle, new_box.bottom ())); + index2++; + if (index2 == blobindex - blobs_per_segment / 2 + || index2 == blobcount - 1) { + xstarts[segment] = new_box.left (); + } + } + lms.fit (b, c); + coeffs[segment * 3 - 3] = 0; + coeffs[segment * 3 - 2] = b; + coeffs[segment * 3 - 1] = c; + segment++; + } + while (segment <= segments); + return coeffs; +} + + +/********************************************************************** + * assign_blobs_to_rows + * + * Make enough rows to allocate all the given blobs to one. + * If a block skew is given, use that, else attempt to track it. + **********************************************************************/ + +void assign_blobs_to_rows( //find lines + TO_BLOCK *block, //block to do + float *gradient, //block skew + int pass, //identification + BOOL8 reject_misses, //chuck big ones out + BOOL8 make_new_rows, //add rows for unmatched + BOOL8 drawing_skew //draw smoothed skew + ) { + OVERLAP_STATE overlap_result; //what to do with it + float ycoord; //current y + float top, bottom; //of blob + float g_length = 1.0f; //from gradient + INT16 row_count; //no of rows + INT16 left_x; //left edge + INT16 last_x; //previous edge + float block_skew; //y delta + float smooth_factor; //for new coords + float near_dist; //dist to nearest row + ICOORD testpt; //testing only + BLOBNBOX *blob; //current blob + TO_ROW *row; //current row + TO_ROW *dest_row; //row to put blob in + //iterators + BLOBNBOX_IT blob_it = &block->blobs; + TO_ROW_IT row_it = block->get_rows (); + + ycoord = + (block->block->bounding_box ().bottom () + + block->block->bounding_box ().top ()) / 2.0f; + if (gradient != NULL) + g_length = sqrt (1 + *gradient * *gradient); +#ifndef GRAPHICS_DISABLED + if (drawing_skew) + move2d (to_win, block->block->bounding_box ().left (), ycoord); +#endif + testpt = ICOORD (textord_test_x, textord_test_y); + blob_it.sort (blob_x_order); + smooth_factor = 1.0; + block_skew = 0.0f; + row_count = row_it.length (); //might have rows + if (!blob_it.empty ()) { + left_x = blob_it.data ()->bounding_box ().left (); + } + else { + left_x = block->block->bounding_box ().left (); + } + last_x = left_x; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (gradient != NULL) { + block_skew = (1 - 1 / g_length) * blob->bounding_box ().bottom () + + *gradient / g_length * blob->bounding_box ().left (); + } + else if (blob->bounding_box ().left () - last_x > block->line_size / 2 + && last_x - left_x > block->line_size * 2 + && textord_interpolating_skew) { + // tprintf("Interpolating skew from %g",block_skew); + block_skew *= (float) (blob->bounding_box ().left () - left_x) + / (last_x - left_x); + // tprintf("to %g\n",block_skew); + } + last_x = blob->bounding_box ().left (); + top = blob->bounding_box ().top () - block_skew; + bottom = blob->bounding_box ().bottom () - block_skew; +#ifndef GRAPHICS_DISABLED + if (drawing_skew) + draw2d (to_win, blob->bounding_box ().left (), ycoord + block_skew); +#endif + if (!row_it.empty ()) { + for (row_it.move_to_first (); + !row_it.at_last () && row_it.data ()->min_y () > top; + row_it.forward ()); + row = row_it.data (); + if (row->min_y () <= top && row->max_y () >= bottom) { + //any overlap + dest_row = row; + overlap_result = most_overlapping_row (&row_it, dest_row, + top, bottom, + block->line_size, + blob->bounding_box (). + contains (testpt)); + if (overlap_result == NEW_ROW && !reject_misses) + overlap_result = ASSIGN; + } + else { + overlap_result = NEW_ROW; + if (!make_new_rows) { + near_dist = row_it.data_relative (-1)->min_y () - top; + //below bottom + if (bottom < row->min_y ()) { + if (row->min_y () - bottom <= + (block->line_spacing - + block->line_size) * textord_merge_desc) { + //done it + overlap_result = ASSIGN; + dest_row = row; + } + } + else if (near_dist > 0 + && near_dist < bottom - row->max_y ()) { + row_it.backward (); + dest_row = row_it.data (); + if (dest_row->min_y () - bottom <= + (block->line_spacing - + block->line_size) * textord_merge_desc) { + //done it + overlap_result = ASSIGN; + } + } + else { + if (top - row->max_y () <= + (block->line_spacing - + block->line_size) * (textord_merge_x + + textord_merge_asc)) { + //done it + overlap_result = ASSIGN; + dest_row = row; + } + } + } + } + if (overlap_result == ASSIGN) + dest_row->add_blob (blob_it.extract (), top, bottom, + block->line_size); + if (overlap_result == NEW_ROW) { + if (make_new_rows && top - bottom < block->max_blob_size) { + dest_row = + new TO_ROW (blob_it.extract (), top, bottom, + block->line_size); + row_count++; + if (bottom > row_it.data ()->min_y ()) + row_it.add_before_then_move (dest_row); + //insert in right place + else + row_it.add_after_then_move (dest_row); + smooth_factor = + 1.0 / (row_count * textord_skew_lag + + textord_skewsmooth_offset); + } + else + overlap_result = REJECT; + } + } + else if (make_new_rows && top - bottom < block->max_blob_size) { + overlap_result = NEW_ROW; + dest_row = + new TO_ROW (blob_it.extract (), top, bottom, block->line_size); + row_count++; + row_it.add_after_then_move (dest_row); + smooth_factor = 1.0 / (row_count * textord_skew_lag + 1); + } + else + overlap_result = REJECT; + if (blob->bounding_box ().contains (testpt)) { + if (overlap_result != REJECT) { + tprintf ("Test blob assigned to row at (%g,%g) on pass %d\n", + dest_row->min_y (), dest_row->max_y (), pass); + } + else { + tprintf ("Test blob assigned to no row on pass %d\n", pass); + } + } + if (overlap_result != REJECT) { + while (!row_it.at_first () + && row_it.data ()->min_y () > + row_it.data_relative (-1)->min_y ()) { + row = row_it.extract (); + row_it.backward (); + row_it.add_before_then_move (row); + } + while (!row_it.at_last () + && row_it.data ()->min_y () < + row_it.data_relative (1)->min_y ()) { + row = row_it.extract (); + row_it.forward (); + //keep rows in order + row_it.add_after_then_move (row); + } + block_skew = (1 - smooth_factor) * block_skew + + smooth_factor * (blob->bounding_box ().bottom () - + dest_row->initial_min_y ()); + } + } + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + if (row_it.data ()->blob_list ()->empty ()) + delete row_it.extract (); //discard empty rows + } +} + + +/********************************************************************** + * most_overlapping_row + * + * Return the row which most overlaps the blob. + **********************************************************************/ + +OVERLAP_STATE most_overlapping_row( //find best row + TO_ROW_IT *row_it, //iterator + TO_ROW *&best_row, //output row + float top, //top of blob + float bottom, //bottom of blob + float rowsize, //max row size + BOOL8 testing_blob //test stuff + ) { + OVERLAP_STATE result; //result of tests + float overlap; //of blob & row + float bestover; //nearest row + float merge_top, merge_bottom; //size of merged row + ICOORD testpt; //testing only + TO_ROW *row; //current row + TO_ROW *test_row; //for multiple overlaps + BLOBNBOX_IT blob_it; //for merging rows + + result = ASSIGN; + row = row_it->data (); + bestover = top - bottom; + if (top > row->max_y ()) + bestover -= top - row->max_y (); + if (bottom < row->min_y ()) + //compute overlap + bestover -= row->min_y () - bottom; + if (testing_blob) { + tprintf ("Test blob y=(%g,%g), row=(%f,%f), overlap=%f\n", + bottom, top, row->min_y (), row->max_y (), bestover); + } + test_row = row; + do { + if (!row_it->at_last ()) { + row_it->forward (); + test_row = row_it->data (); + if (test_row->min_y () <= top && test_row->max_y () >= bottom) { + merge_top = + test_row->max_y () > + row->max_y ()? test_row->max_y () : row->max_y (); + merge_bottom = + test_row->min_y () < + row->min_y ()? test_row->min_y () : row->min_y (); + if (merge_top - merge_bottom <= rowsize) { + if (testing_blob) { + tprintf ("Merging rows at (%g,%g), (%g,%g)\n", + row->min_y (), row->max_y (), + test_row->min_y (), test_row->max_y ()); + } + test_row->set_limits (merge_bottom, merge_top); + blob_it.set_to_list (test_row->blob_list ()); + blob_it.add_list_after (row->blob_list ()); + blob_it.sort (blob_x_order); + row_it->backward (); + delete row_it->extract (); + row_it->forward (); + bestover = -1.0f; //force replacement + } + overlap = top - bottom; + if (top > test_row->max_y ()) + overlap -= top - test_row->max_y (); + if (bottom < test_row->min_y ()) + overlap -= test_row->min_y () - bottom; + if (bestover >= rowsize - 1 && overlap >= rowsize - 1) { + result = REJECT; + } + if (overlap > bestover) { + bestover = overlap; //find biggest overlap + row = test_row; + } + if (testing_blob) { + tprintf + ("Test blob y=(%g,%g), row=(%f,%f), overlap=%f->%f\n", + bottom, top, test_row->min_y (), test_row->max_y (), + overlap, bestover); + } + } + } + } + while (!row_it->at_last () + && test_row->min_y () <= top && test_row->max_y () >= bottom); + while (row_it->data () != row) + row_it->backward (); //make it point to row + //doesn't overlap much + if (top - bottom - bestover > rowsize * textord_merge_x && (!textord_fix_makerow_bug || bestover < rowsize * textord_merge_x) + && result == ASSIGN) + result = NEW_ROW; //doesn't overlap enough + best_row = row; + return result; +} + + +/********************************************************************** + * blob_x_order + * + * Sort function to sort blobs in x from page left. + **********************************************************************/ + +int blob_x_order( //sort function + const void *item1, //items to compare + const void *item2) { + //converted ptr + BLOBNBOX *blob1 = *(BLOBNBOX **) item1; + //converted ptr + BLOBNBOX *blob2 = *(BLOBNBOX **) item2; + + if (blob1->bounding_box ().left () < blob2->bounding_box ().left ()) + return -1; + else if (blob1->bounding_box ().left () > blob2->bounding_box ().left ()) + return 1; + else + return 0; +} + + +/********************************************************************** + * row_y_order + * + * Sort function to sort rows in y from page top. + **********************************************************************/ + +int row_y_order( //sort function + const void *item1, //items to compare + const void *item2) { + //converted ptr + TO_ROW *row1 = *(TO_ROW **) item1; + //converted ptr + TO_ROW *row2 = *(TO_ROW **) item2; + + if (row1->parallel_c () > row2->parallel_c ()) + return -1; + else if (row1->parallel_c () < row2->parallel_c ()) + return 1; + else + return 0; +} + + +/********************************************************************** + * row_spacing_order + * + * Qsort style function to compare 2 TO_ROWS based on their spacing value. + **********************************************************************/ + +int row_spacing_order( //sort function + const void *item1, //items to compare + const void *item2) { + //converted ptr + TO_ROW *row1 = *(TO_ROW **) item1; + //converted ptr + TO_ROW *row2 = *(TO_ROW **) item2; + + if (row1->spacing < row2->spacing) + return -1; + else if (row1->spacing > row2->spacing) + return 1; + else + return 0; +} diff --git a/textord/makerow.h b/textord/makerow.h new file mode 100644 index 0000000000..2c766fee2b --- /dev/null +++ b/textord/makerow.h @@ -0,0 +1,295 @@ +/********************************************************************** + * File: makerow.h (Formerly makerows.h) + * Description: Code to arrange blobs into rows of text. + * Author: Ray Smith + * Created: Mon Sep 21 14:34:48 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef MAKEROW_H +#define MAKEROW_H + +#include "varable.h" +#include "ocrblock.h" +#include "tessclas.h" +#include "blobbox.h" +#include "statistc.h" +#include "notdll.h" + +enum OVERLAP_STATE +{ + ASSIGN, //assign it to row + REJECT, //reject it - dual overlap + NEW_ROW +}; + +extern BOOL_VAR_H (textord_show_initial_rows, FALSE, +"Display row accumulation"); +extern BOOL_VAR_H (textord_show_parallel_rows, FALSE, +"Display page correlated rows"); +extern BOOL_VAR_H (textord_show_expanded_rows, FALSE, +"Display rows after expanding"); +extern BOOL_VAR_H (textord_show_final_rows, FALSE, +"Display rows after final fittin"); +extern BOOL_VAR_H (textord_show_final_blobs, FALSE, +"Display blob bounds after pre-ass"); +extern BOOL_VAR_H (textord_test_landscape, FALSE, "Tests refer to land/port"); +extern BOOL_VAR_H (textord_parallel_baselines, TRUE, +"Force parallel baselines"); +extern BOOL_VAR_H (textord_straight_baselines, FALSE, +"Force straight baselines"); +extern BOOL_VAR_H (textord_quadratic_baselines, FALSE, +"Use quadratic splines"); +extern BOOL_VAR_H (textord_old_baselines, TRUE, "Use old baseline algorithm"); +extern BOOL_VAR_H (textord_old_xheight, TRUE, "Use old xheight algorithm"); +extern BOOL_VAR_H (textord_fix_xheight_bug, TRUE, "Use spline baseline"); +extern BOOL_VAR_H (textord_fix_makerow_bug, TRUE, +"Prevent multiple baselines"); +extern BOOL_VAR_H (textord_row_xheights, FALSE, "Use row height policy"); +extern BOOL_VAR_H (textord_block_xheights, TRUE, "Use block height policy"); +extern BOOL_VAR_H (textord_xheight_tweak, FALSE, +"New min condition on height"); +extern BOOL_VAR_H (textord_cblob_blockocc, TRUE, +"Use new projection for underlines"); +extern BOOL_VAR_H (textord_debug_xheights, FALSE, "Test xheight algorithms"); +extern INT_VAR_H (textord_test_x, 0, "coord of test pt"); +extern INT_VAR_H (textord_test_y, 0, "coord of test pt"); +extern INT_VAR_H (textord_min_blobs_in_row, 4, +"Min blobs before gradient counted"); +extern INT_VAR_H (textord_spline_minblobs, 8, +"Min blobs in each spline segment"); +extern INT_VAR_H (textord_spline_medianwin, 6, +"Size of window for spline segmentation"); +extern INT_VAR_H (textord_min_xheight, 10, "Min credible pixel xheight"); +extern double_VAR_H (textord_spline_shift_fraction, 0.02, +"Fraction of line spacing for quad"); +extern double_VAR_H (textord_spline_outlier_fraction, 0.1, +"Fraction of line spacing for outlier"); +extern double_VAR_H (textord_skew_ile, 0.5, "Ile of gradients for page skew"); +extern double_VAR_H (textord_skew_lag, 0.75, +"Lag for skew on row accumulation"); +extern double_VAR_H (textord_linespace_iqrlimit, 0.2, +"Max iqr/median for linespace"); +extern double_VAR_H (textord_width_limit, 8, +"Max width of blobs to make rows"); +extern double_VAR_H (textord_chop_width, 1.5, "Max width before chopping"); +extern double_VAR_H (textord_merge_desc, 0.25, +"Fraction of linespace for desc drop"); +extern double_VAR_H (textord_merge_x, 0.5, +"Fraction of linespace for x height"); +extern double_VAR_H (textord_merge_asc, 0.25, +"Fraction of linespace for asc height"); +extern double_VAR_H (textord_minxh, 0.25, +"fraction of linesize for min xheight"); +extern double_VAR_H (textord_min_linesize, 1.25, +"* blob height for initial linesize"); +extern double_VAR_H (textord_excess_blobsize, 1.3, +"New row made if blob makes row this big"); +extern double_VAR_H (textord_occupancy_threshold, 0.4, +"Fraction of neighbourhood"); +extern double_VAR_H (textord_underline_width, 2.0, +"Multiple of line_size for underline"); +extern double_VAR_H (textord_xheight_mode_fraction, 0.4, +"Min pile height to make xheight"); +extern double_VAR_H (textord_ascheight_mode_fraction, 0.15, +"Min pile height to make ascheight"); +extern double_VAR_H (textord_ascx_ratio_min, 1.2, "Min cap/xheight"); +extern double_VAR_H (textord_ascx_ratio_max, 1.7, "Max cap/xheight"); +extern double_VAR_H (textord_descx_ratio_min, 0.15, "Min desc/xheight"); +extern double_VAR_H (textord_descx_ratio_max, 0.6, "Max desc/xheight"); +extern double_VAR_H (textord_xheight_error_margin, 0.1, "Accepted variation"); +float make_rows( //make rows + ICOORD page_tr, //top right + BLOCK_LIST *blocks, //block list + TO_BLOCK_LIST *land_blocks, //rotated for landscape + TO_BLOCK_LIST *port_blocks //output list + ); +void make_initial_textrows( //find lines + ICOORD page_tr, + TO_BLOCK *block, //block to do + FCOORD rotation, //for drawing + BOOL8 testing_on //correct orientation + ); +void fit_lms_line( //sort function + TO_ROW *row //row to fit + ); +void compute_page_skew( //get average gradient + TO_BLOCK_LIST *blocks, //list of blocks + float &page_m, //average gradient + float &page_err //average error + ); +void cleanup_rows( //find lines + ICOORD page_tr, //top right + TO_BLOCK *block, //block to do + float gradient, //gradient to fit + FCOORD rotation, //for drawing + INT32 block_edge, //edge of block + BOOL8 testing_on //correct orientation + ); +void delete_non_dropout_rows( //find lines + TO_BLOCK *block, //block to do + float gradient, //global skew + FCOORD rotation, //deskew vector + INT32 block_edge, //left edge + BOOL8 testing_on //correct orientation + ); +BOOL8 find_best_dropout_row( //find neighbours + TO_ROW *row, //row to test + INT32 distance, //dropout dist + float dist_limit, //threshold distance + INT32 line_index, //index of row + TO_ROW_IT *row_it, //current position + BOOL8 testing_on //correct orientation + ); +BOX deskew_block_coords( //block box + TO_BLOCK *block, //block to do + float gradient //global skew + ); +void compute_line_occupation( //project blobs + TO_BLOCK *block, //block to do + float gradient, //global skew + INT32 min_y, //min coord in block + INT32 max_y, //in block + INT32 *occupation, //output projection + INT32 *deltas //derivative + ); +void compute_occupation_threshold( //project blobs + INT32 low_window, //below result point + INT32 high_window, //above result point + INT32 line_count, //array sizes + INT32 *occupation, //input projection + INT32 *thresholds //output thresholds + ); +void compute_dropout_distances( //project blobs + INT32 *occupation, //input projection + INT32 *thresholds, //output thresholds + INT32 line_count //array sizes + ); +void expand_rows( //find lines + ICOORD page_tr, //top right + TO_BLOCK *block, //block to do + float gradient, //gradient to fit + FCOORD rotation, //for drawing + INT32 block_edge, //edge of block + BOOL8 testing_on //correct orientation + ); +void adjust_row_limits( //tidy limits + TO_BLOCK *block //block to do + ); +void compute_row_stats( //find lines + TO_BLOCK *block, //block to do + BOOL8 testing_on //correct orientation + ); +void compute_block_xheight( //find lines + TO_BLOCK *block, //block to do + float gradient //global skew + ); +float median_block_xheight( //find lines + TO_BLOCK *block, //block to do + float gradient //global skew + ); +INT32 compute_row_xheight( //find lines + TO_ROW *row, //row to do + INT32 min_height, //min xheight + INT32 max_height, //max xheight + float gradient //global skew + ); +INT32 compute_row_descdrop( //find lines + TO_ROW *row, //row to do + float gradient //global skew + ); +INT32 compute_height_modes( //find lines + STATS *heights, //stats to search + INT32 min_height, //bottom of range + INT32 max_height, //top of range + INT32 *modes, //output array + INT32 maxmodes //size of modes + ); +void correct_row_xheight( //fix bad values + TO_ROW *row, //row to fix + float xheight, //average values + float ascrise, + float descdrop); +void separate_underlines( //make rough chars + TO_BLOCK *block, //block to do + float gradient, //skew angle + FCOORD rotation, //inverse landscape + BOOL8 testing_on //correct orientation + ); +void pre_associate_blobs( //make rough chars + ICOORD page_tr, //top right + TO_BLOCK *block, //block to do + FCOORD rotation, //inverse landscape + BOOL8 testing_on //correct orientation + ); +void fit_parallel_rows( //find lines + TO_BLOCK *block, //block to do + float gradient, //gradient to fit + FCOORD rotation, //for drawing + INT32 block_edge, //edge of block + BOOL8 testing_on //correct orientation + ); +void fit_parallel_lms( //sort function + float gradient, //forced gradient + TO_ROW *row //row to fit + ); +void make_spline_rows( //find lines + TO_BLOCK *block, //block to do + float gradient, //gradient to fit + FCOORD rotation, //for drawing + INT32 block_edge, //edge of block + BOOL8 testing_on //correct orientation + ); +void make_baseline_spline( //sort function + TO_ROW *row, //row to fit + TO_BLOCK *block //block it came from + ); +BOOL8 segment_baseline ( //split baseline +TO_ROW * row, //row to fit +TO_BLOCK * block, //block it came from +INT32 & segments, //no fo segments +INT32 xstarts[] //coords of segments +); +double *linear_spline_baseline ( //split baseline +TO_ROW * row, //row to fit +TO_BLOCK * block, //block it came from +INT32 & segments, //no fo segments +INT32 xstarts[] //coords of segments +); +void assign_blobs_to_rows( //find lines + TO_BLOCK *block, //block to do + float *gradient, //block skew + int pass, //identification + BOOL8 reject_misses, //chuck big ones out + BOOL8 make_new_rows, //add rows for unmatched + BOOL8 drawing_skew //draw smoothed skew + ); + //find best row +OVERLAP_STATE most_overlapping_row(TO_ROW_IT *row_it, //iterator + TO_ROW *&best_row, //output row + float top, //top of blob + float bottom, //bottom of blob + float rowsize, //max row size + BOOL8 testing_blob //test stuff + ); +int blob_x_order( //sort function + const void *item1, //items to compare + const void *item2); +int row_y_order( //sort function + const void *item1, //items to compare + const void *item2); +int row_spacing_order( //sort function + const void *item1, //items to compare + const void *item2); +#endif diff --git a/textord/oldbasel.cpp b/textord/oldbasel.cpp new file mode 100644 index 0000000000..72b76ac92d --- /dev/null +++ b/textord/oldbasel.cpp @@ -0,0 +1,1759 @@ +/********************************************************************** + * File: oldbasel.cpp (Formerly oldbl.c) + * Description: A re-implementation of the old baseline algorithm. + * Author: Ray Smith + * Created: Wed Oct 6 09:41:48 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "statistc.h" +#include "quadlsq.h" +#include "lmedsq.h" +#include "makerow.h" +#include "drawtord.h" +#include "oldbasel.h" +#include "tprintf.h" + +#define EXTERN + +EXTERN BOOL_VAR (textord_really_old_xheight, FALSE, +"Use original wiseowl xheight"); +EXTERN BOOL_VAR (textord_oldbl_debug, FALSE, "Debug old baseline generation"); +EXTERN BOOL_VAR (textord_debug_baselines, FALSE, "Debug baseline generation"); +EXTERN BOOL_VAR (textord_oldbl_paradef, TRUE, "Use para default mechanism"); +EXTERN BOOL_VAR (textord_oldbl_split_splines, TRUE, "Split stepped splines"); +EXTERN BOOL_VAR (textord_oldbl_merge_parts, TRUE, "Merge suspect partitions"); +EXTERN BOOL_VAR (oldbl_corrfix, TRUE, "Improve correlation of heights"); +EXTERN BOOL_VAR (oldbl_xhfix, FALSE, +"Fix bug in modes threshold for xheights"); +EXTERN double_VAR (oldbl_xhfract, 0.4, "Fraction of est allowed in calc"); +EXTERN INT_VAR (oldbl_holed_losscount, 10, +"Max lost before fallback line used"); +EXTERN double_VAR (oldbl_dot_error_size, 1.26, "Max aspect ratio of a dot"); +EXTERN double_VAR (textord_oldbl_jumplimit, 0.15, +"X fraction for new partition"); + +#define TURNLIMIT 1 /*min size for turning point */ +#define X_HEIGHT_FRACTION 0.7 /*x-height/caps height */ +#define DESCENDER_FRACTION 0.5 /*descender/x-height */ +#define MIN_ASC_FRACTION 0.20 /*min size of ascenders */ +#define MIN_DESC_FRACTION 0.25 /*min size of descenders */ +#define MINASCRISE 2.0 /*min ascender/desc step */ +#define MAXHEIGHTVARIANCE 0.15 /*accepted variation in x-height */ +#define MAXHEIGHT 300 /*max blob height */ +#define MAXOVERLAP 0.1 /*max 10% missed overlap */ +#define MAXBADRUN 2 /*max non best for failed */ +#define HEIGHTBUCKETS 200 /* Num of buckets */ +#define DELTAHEIGHT 5.0 /* Small amount of diff */ +#define GOODHEIGHT 5 +#define MAXLOOPS 10 +#define MODENUM 10 +#define MAXPARTS 6 +#define SPLINESIZE 23 + +#define ABS(x) ((x)<0 ? (-(x)) : (x)) + +/********************************************************************** + * make_old_baselines + * + * Top level function to make baselines the old way. + **********************************************************************/ + +void make_old_baselines( //make splines + TO_BLOCK *block, //block to do + BOOL8 testing_on //correct orientation + ) { + QSPLINE *prev_baseline; //baseline of previous row + TO_ROW *row; //current row + TO_ROW_IT row_it = block->get_rows (); + BLOBNBOX_IT blob_it; + + prev_baseline = NULL; //nothing yet + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + find_textlines (block, row, 2, NULL); + if (row->xheight <= 0 && prev_baseline != NULL) + find_textlines (block, row, 2, prev_baseline); + if (row->xheight > 0) + //was a good one + prev_baseline = &row->baseline; + else { + prev_baseline = NULL; + blob_it.set_to_list (row->blob_list ()); + if (textord_debug_baselines) + tprintf ("Row baseline generation failed on row at (%d,%d)\n", + blob_it.data ()->bounding_box ().left (), + blob_it.data ()->bounding_box ().bottom ()); + } + } + correlate_lines(block); +} + + +/********************************************************************** + * correlate_lines + * + * Correlate the x-heights and ascender heights of a block to fill-in + * the ascender height and descender height for rows without one. + * Also fix baselines of rows without a decent fit. + **********************************************************************/ + +void correlate_lines( //cleanup lines + TO_BLOCK *block //block to do + ) { + TO_ROW **rows; //array of ptrs + int rowcount; /*no of rows to do */ + register int rowindex; /*no of row */ + //iterator + TO_ROW_IT row_it = block->get_rows (); + + rowcount = row_it.length (); + if (rowcount == 0) { + //default value + block->xheight = block->line_size; + return; /*none to do */ + } + rows = (TO_ROW **) alloc_mem (rowcount * sizeof (TO_ROW *)); + rowindex = 0; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) + //make array + rows[rowindex++] = row_it.data (); + + /*try to fix bad lines */ + correlate_neighbours(block, rows, rowcount); + + block->xheight = (float) correlate_with_stats (rows, rowcount); + /*use stats */ + if (block->xheight <= 0) + //desperate + block->xheight = block->line_size * textord_merge_x; + if (block->xheight < textord_min_xheight) + block->xheight = (float) textord_min_xheight; + + free_mem(rows); +} + + +/********************************************************************** + * correlate_neighbours + * + * Try to fix rows that had a bad spline fit by using neighbours. + **********************************************************************/ + +void correlate_neighbours( //fix bad rows + TO_BLOCK *block, /*block rows are in */ + TO_ROW **rows, /*rows of block */ + int rowcount /*no of rows to do */ + ) { + TO_ROW *row; /*current row */ + register int rowindex; /*no of row */ + register int otherrow; /*second row */ + int upperrow; /*row above to use */ + int lowerrow; /*row below to use */ + float biggest; + + for (rowindex = 0; rowindex < rowcount; rowindex++) { + row = rows[rowindex]; /*current row */ + if (row->xheight < 0) { + /*quadratic failed */ + for (otherrow = rowindex - 2; + otherrow >= 0 + && (rows[otherrow]->xheight < 0.0 + || !row->baseline.overlap (&rows[otherrow]->baseline, + MAXOVERLAP)); otherrow--); + upperrow = otherrow; /*decent row above */ + for (otherrow = rowindex + 1; + otherrow < rowcount + && (rows[otherrow]->xheight < 0.0 + || !row->baseline.overlap (&rows[otherrow]->baseline, + MAXOVERLAP)); otherrow++); + lowerrow = otherrow; /*decent row below */ + if (upperrow >= 0) + find_textlines (block, row, 2, &rows[upperrow]->baseline); + if (row->xheight < 0 && lowerrow < rowcount) + find_textlines (block, row, 2, &rows[lowerrow]->baseline); + if (row->xheight < 0) { + if (upperrow >= 0) + find_textlines (block, row, 1, &rows[upperrow]->baseline); + else if (lowerrow < rowcount) + find_textlines (block, row, 1, &rows[lowerrow]->baseline); + } + } + } + + for (biggest = 0.0f, rowindex = 0; rowindex < rowcount; rowindex++) { + row = rows[rowindex]; /*current row */ + if (row->xheight < 0) /*linear failed */ + /*make do */ + row->xheight = -row->xheight; + biggest = MAX (biggest, row->xheight); + } +} + + +/********************************************************************** + * correlate_with_stats + * + * correlate the x-heights and ascender heights of a block to fill-in + * the ascender height and descender height for rows without one. + **********************************************************************/ + +int correlate_with_stats( //fix xheights + TO_ROW **rows, /*rows of block */ + int rowcount /*no of rows to do */ + ) { + TO_ROW *row; /*current row */ + register int rowindex; /*no of row */ + float lineheight; /*mean x-height */ + float ascheight; /*average ascenders */ + float minascheight; /*min allowed ascheight */ + int xcount; /*no of samples for xheight */ + float fullheight; /*mean top height */ + int fullcount; /*no of samples */ + float descheight; /*mean descender drop */ + float mindescheight; /*min allowed descheight */ + int desccount; /*no of samples */ + float xshift; /*shift in xheight */ + + /*no samples */ + xcount = fullcount = desccount = 0; + lineheight = ascheight = fullheight = descheight = 0.0; + for (rowindex = 0; rowindex < rowcount; rowindex++) { + row = rows[rowindex]; /*current row */ + if (row->ascrise > 0.0) { /*got ascenders? */ + lineheight += row->xheight;/*average x-heights */ + ascheight += row->ascrise; /*average ascenders */ + xcount++; + } + else { + fullheight += row->xheight;/*assume full height */ + fullcount++; + } + if (row->descdrop < 0.0) { /*got descenders? */ + /*average descenders */ + descheight += row->descdrop; + desccount++; + } + } + + if (xcount > 0 && (!oldbl_corrfix || xcount >= fullcount)) { + lineheight /= xcount; /*average x-height */ + /*average caps height */ + fullheight = lineheight + ascheight / xcount; + /*must be decent size */ + if (fullheight < lineheight * (1 + MIN_ASC_FRACTION)) + fullheight = lineheight * (1 + MIN_ASC_FRACTION); + } + else { + fullheight /= fullcount; /*average max height */ + /*guess x-height */ + lineheight = fullheight * X_HEIGHT_FRACTION; + } + if (desccount > 0 && (!oldbl_corrfix || desccount >= rowcount / 2)) + descheight /= desccount; /*average descenders */ + else + /*guess descenders */ + descheight = -lineheight * DESCENDER_FRACTION; + + minascheight = lineheight * MIN_ASC_FRACTION; + mindescheight = -lineheight * MIN_DESC_FRACTION; + for (rowindex = 0; rowindex < rowcount; rowindex++) { + row = rows[rowindex]; /*do each row */ + row->all_caps = FALSE; + if (row->ascrise / row->xheight < MIN_ASC_FRACTION) { + /*no ascenders */ + if (row->xheight >= lineheight * (1 - MAXHEIGHTVARIANCE) + && row->xheight <= lineheight * (1 + MAXHEIGHTVARIANCE)) { + row->ascrise = fullheight - lineheight; + /*shift in x */ + xshift = lineheight - row->xheight; + /*set to average */ + row->xheight = lineheight; + + } + else if (row->xheight >= fullheight * (1 - MAXHEIGHTVARIANCE) + && row->xheight <= fullheight * (1 + MAXHEIGHTVARIANCE)) { + row->ascrise = row->xheight - lineheight; + xshift = -row->ascrise; /*shift in x */ + /*set to average */ + row->xheight = lineheight; + row->all_caps = TRUE; + } + else { + row->ascrise = (fullheight - lineheight) * row->xheight + / fullheight; + xshift = -row->ascrise; /*shift in x */ + /*scale it */ + row->xheight -= row->ascrise; + row->all_caps = TRUE; + } + if (row->ascrise < minascheight) + row->ascrise = + row->xheight * ((1.0 - X_HEIGHT_FRACTION) / X_HEIGHT_FRACTION); + } + if (row->descdrop > mindescheight) { + if (row->xheight >= lineheight * (1 - MAXHEIGHTVARIANCE) + && row->xheight <= lineheight * (1 + MAXHEIGHTVARIANCE)) + /*set to average */ + row->descdrop = descheight; + else + row->descdrop = -row->xheight * DESCENDER_FRACTION; + } + } + return (int) lineheight; //block xheight +} + + +/********************************************************************** + * find_textlines + * + * Compute the baseline for the given row. + **********************************************************************/ + +void find_textlines( //get baseline + TO_BLOCK *block, //block row is in + TO_ROW *row, //row to do + int degree, //required approximation + QSPLINE *spline //starting spline + ) { + int partcount; /*no of partitions of */ + BOOL8 holed_line; //lost too many blobs + int bestpart; /*biggest partition */ + char *partids; /*partition no of each blob */ + int partsizes[MAXPARTS]; /*no in each partition */ + int lineheight; /*guessed x-height */ + float jumplimit; /*allowed delta change */ + int *xcoords; /*useful sample points */ + int *ycoords; /*useful sample points */ + BOX *blobcoords; /*edges of blob rectangles */ + int blobcount; /*no of blobs on line */ + float *ydiffs; /*diffs from 1st approx */ + int pointcount; /*no of coords */ + int xstarts[SPLINESIZE + 1]; //segment boundaries + int segments; //no of segments + + //no of blobs in row + blobcount = row->blob_list ()->length (); + partids = (char *) alloc_mem (blobcount * sizeof (char)); + xcoords = (int *) alloc_mem (blobcount * sizeof (int)); + ycoords = (int *) alloc_mem (blobcount * sizeof (int)); + blobcoords = (BOX *) alloc_mem (blobcount * sizeof (BOX)); + ydiffs = (float *) alloc_mem (blobcount * sizeof (float)); + + lineheight = get_blob_coords (row, (int) block->line_size, blobcoords, + holed_line, blobcount); + /*limit for line change */ + jumplimit = lineheight * textord_oldbl_jumplimit; + if (jumplimit < MINASCRISE) + jumplimit = MINASCRISE; + + if (textord_oldbl_debug) { + tprintf + ("\nInput height=%g, Estimate x-height=%d pixels, jumplimit=%.2f\n", + block->line_size, lineheight, jumplimit); + } + if (holed_line) + make_holed_baseline (blobcoords, blobcount, spline, &row->baseline, + row->line_m ()); + else + make_first_baseline (blobcoords, blobcount, + xcoords, ycoords, spline, &row->baseline, jumplimit); +#ifndef GRAPHICS_DISABLED + if (textord_show_final_rows) + row->baseline.plot (to_win, GOLDENROD); +#endif + if (blobcount > 1) { + bestpart = partition_line (blobcoords, blobcount, + &partcount, partids, partsizes, + &row->baseline, jumplimit, ydiffs); + pointcount = partition_coords (blobcoords, blobcount, + partids, bestpart, xcoords, ycoords); + segments = segment_spline (blobcoords, blobcount, + xcoords, ycoords, + degree, pointcount, xstarts); + if (!holed_line) { + do { + row->baseline = QSPLINE (xstarts, segments, + xcoords, ycoords, pointcount, degree); + } + while (textord_oldbl_split_splines + && split_stepped_spline (&row->baseline, jumplimit / 2, + xcoords, xstarts, segments)); + } + find_lesser_parts(row, + blobcoords, + blobcount, + partids, + partsizes, + partcount, + bestpart); + + } + else { + row->xheight = -1.0f; /*failed */ + row->descdrop = 0.0f; + row->ascrise = 0.0f; + } + row->baseline.extrapolate (row->line_m (), + block->block->bounding_box ().left (), + block->block->bounding_box ().right ()); + if (textord_really_old_xheight) + old_first_xheight (row, blobcoords, lineheight, + blobcount, &row->baseline, jumplimit); + else + make_first_xheight (row, blobcoords, lineheight, (int) block->line_size, + blobcount, &row->baseline, jumplimit); + free_mem(partids); + free_mem(xcoords); + free_mem(ycoords); + free_mem(blobcoords); + free_mem(ydiffs); +} + + +/********************************************************************** + * get_blob_coords + * + * Fill the blobcoords array with the coordinates of the blobs + * in the row. The return value is the first guess atthe line height. + **********************************************************************/ + +int get_blob_coords( //get boxes + TO_ROW *row, //row to use + INT32 lineheight, //block level + BOX *blobcoords, //ouput boxes + BOOL8 &holed_line, //lost a lot of blobs + int &outcount //no of real blobs + ) { + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + register int blobindex; /*no along text line */ + int losscount; //lost blobs + int maxlosscount; //greatest lost blobs + /*height stat collection */ + STATS heightstat (0, MAXHEIGHT); + + if (blob_it.empty ()) + return 0; //none + maxlosscount = 0; + losscount = 0; + blob_it.mark_cycle_pt (); + blobindex = 0; + do { + blobcoords[blobindex] = box_next_pre_chopped (&blob_it); + if (blobcoords[blobindex].height () > lineheight * 0.25) + heightstat.add (blobcoords[blobindex].height (), 1); + if (blobindex == 0 + || blobcoords[blobindex].height () > lineheight * 0.25 + || blob_it.cycled_list ()) { + blobindex++; /*no of merged blobs */ + losscount = 0; + } + else { + if (blobcoords[blobindex].height () + < blobcoords[blobindex].width () * oldbl_dot_error_size + && blobcoords[blobindex].width () + < blobcoords[blobindex].height () * oldbl_dot_error_size) { + //counts as dot + blobindex++; + losscount = 0; + } + else { + losscount++; //lost it + if (losscount > maxlosscount) + //remember max + maxlosscount = losscount; + } + } + } + while (!blob_it.cycled_list ()); + + holed_line = maxlosscount > oldbl_holed_losscount; + outcount = blobindex; /*total blobs */ + + if (heightstat.get_total () > 1) + /*guess x-height */ + return (int) heightstat.ile (0.25); + else + return blobcoords[0].height (); +} + + +/********************************************************************** + * make_first_baseline + * + * Make the first estimate at a baseline, either by shifting + * a supplied previous spline, or by doing a piecewise linear + * approximation using all the blobs. + **********************************************************************/ + +void +make_first_baseline ( //initial approximation +BOX blobcoords[], /*blob bounding boxes */ +int blobcount, /*no of blobcoords */ +int xcoords[], /*coords for spline */ +int ycoords[], /*approximator */ +QSPLINE * spline, /*initial spline */ +QSPLINE * baseline, /*output spline */ +float jumplimit /*guess half descenders */ +) { + int leftedge; /*left edge of line */ + int rightedge; /*right edge of line */ + int blobindex; /*current blob */ + int segment; /*current segment */ + float prevy, thisy, nexty; /*3 y coords */ + float y1, y2, y3; /*3 smooth blobs */ + float maxmax, minmin; /*absolute limits */ + int x2 = 0; /*right edge of old y3 */ + int ycount; /*no of ycoords in use */ + float yturns[SPLINESIZE]; /*y coords of turn pts */ + int xturns[SPLINESIZE]; /*xcoords of turn pts */ + int xstarts[SPLINESIZE + 1]; + int segments; //no of segments + ICOORD shift; //shift of spline + + prevy = 0; + /*left edge of row */ + leftedge = blobcoords[0].left (); + /*right edge of line */ + rightedge = blobcoords[blobcount - 1].right (); + if (spline == NULL /*no given spline */ + || spline->segments < 3 /*or trivial */ + /*or too non-overlap */ + || spline->xcoords[1] > leftedge + MAXOVERLAP * (rightedge - leftedge) + || spline->xcoords[spline->segments - 1] < rightedge + - MAXOVERLAP * (rightedge - leftedge)) { + if (textord_oldbl_paradef) + return; //use default + xstarts[0] = blobcoords[0].left () - 1; + for (blobindex = 0; blobindex < blobcount; blobindex++) { + xcoords[blobindex] = (blobcoords[blobindex].left () + + blobcoords[blobindex].right ()) / 2; + ycoords[blobindex] = blobcoords[blobindex].bottom (); + } + xstarts[1] = blobcoords[blobcount - 1].right () + 1; + segments = 1; /*no of segments */ + + /*linear */ + *baseline = QSPLINE (xstarts, segments, xcoords, ycoords, blobcount, 1); + + if (blobcount >= 3) { + y1 = y2 = y3 = 0.0f; + ycount = 0; + segment = 0; /*no of segments */ + maxmax = minmin = 0.0f; + thisy = ycoords[0] - baseline->y (xcoords[0]); + nexty = ycoords[1] - baseline->y (xcoords[1]); + for (blobindex = 2; blobindex < blobcount; blobindex++) { + prevy = thisy; /*shift ycoords */ + thisy = nexty; + nexty = ycoords[blobindex] - baseline->y (xcoords[blobindex]); + /*middle of smooth y */ + if (ABS (thisy - prevy) < jumplimit && ABS (thisy - nexty) < jumplimit) { + y1 = y2; /*shift window */ + y2 = y3; + y3 = thisy; /*middle point */ + ycount++; + /*local max */ + if (ycount >= 3 && (y1 < y2 && y2 >= y3 + /*local min */ + || y1 > y2 && y2 <= y3)) { + if (segment < SPLINESIZE - 2) { + /*turning pt */ + xturns[segment] = x2; + yturns[segment] = y2; + segment++; /*no of spline segs */ + } + } + if (ycount == 1) { + maxmax = minmin = y3;/*initialise limits */ + } + else { + if (y3 > maxmax) + maxmax = y3; /*biggest max */ + if (y3 < minmin) + minmin = y3; /*smallest min */ + } + /*possible turning pt */ + x2 = blobcoords[blobindex - 1].right (); + } + } + + jumplimit *= 1.2; + /*must be wavy */ + if (maxmax - minmin > jumplimit) { + ycount = segment; /*no of segments */ + for (blobindex = 0, segment = 1; blobindex < ycount; + blobindex++) { + if (yturns[blobindex] > minmin + jumplimit + || yturns[blobindex] < maxmax - jumplimit) { + /*significant peak */ + if (segment == 1 + || yturns[blobindex] > prevy + jumplimit + || yturns[blobindex] < prevy - jumplimit) { + /*different to previous */ + xstarts[segment] = xturns[blobindex]; + segment++; + prevy = yturns[blobindex]; + } + /*bigger max */ + else if (prevy > minmin + jumplimit && yturns[blobindex] > prevy + /*smaller min */ + || prevy < maxmax - jumplimit && yturns[blobindex] < prevy) { + xstarts[segment - 1] = xturns[blobindex]; + /*improved previous */ + prevy = yturns[blobindex]; + } + } + } + xstarts[segment] = blobcoords[blobcount - 1].right () + 1; + segments = segment; /*no of segments */ + /*linear */ + *baseline = QSPLINE (xstarts, segments, xcoords, ycoords, blobcount, 1); + } + } + } + else { + *baseline = *spline; /*copy it */ + shift = ICOORD (0, (INT16) (blobcoords[0].bottom () + - spline->y (blobcoords[0].right ()))); + baseline->move (shift); + } +} + + +/********************************************************************** + * make_holed_baseline + * + * Make the first estimate at a baseline, either by shifting + * a supplied previous spline, or by doing a piecewise linear + * approximation using all the blobs. + **********************************************************************/ + +void +make_holed_baseline ( //initial approximation +BOX blobcoords[], /*blob bounding boxes */ +int blobcount, /*no of blobcoords */ +QSPLINE * spline, /*initial spline */ +QSPLINE * baseline, /*output spline */ +float gradient //of line +) { + int leftedge; /*left edge of line */ + int rightedge; /*right edge of line */ + int blobindex; /*current blob */ + float x; //centre of row + ICOORD shift; //shift of spline + + LMS lms(blobcount); //straight baseline + INT32 xstarts[2]; //straight line + double coeffs[3]; + float c; //line parameter + + /*left edge of row */ + leftedge = blobcoords[0].left (); + /*right edge of line */ + rightedge = blobcoords[blobcount - 1].right (); + for (blobindex = 0; blobindex < blobcount; blobindex++) { + lms.add (FCOORD ((blobcoords[blobindex].left () + + blobcoords[blobindex].right ()) / 2.0, + blobcoords[blobindex].bottom ())); + } + lms.constrained_fit (gradient, c); + xstarts[0] = leftedge; + xstarts[1] = rightedge; + coeffs[0] = 0; + coeffs[1] = gradient; + coeffs[2] = c; + *baseline = QSPLINE (1, xstarts, coeffs); + if (spline != NULL /*no given spline */ + && spline->segments >= 3 /*or trivial */ + /*or too non-overlap */ + && spline->xcoords[1] <= leftedge + MAXOVERLAP * (rightedge - leftedge) + && spline->xcoords[spline->segments - 1] >= rightedge + - MAXOVERLAP * (rightedge - leftedge)) { + *baseline = *spline; /*copy it */ + x = (leftedge + rightedge) / 2.0; + shift = ICOORD (0, (INT16) (gradient * x + c - spline->y (x))); + baseline->move (shift); + } +} + + +/********************************************************************** + * partition_line + * + * Partition a row of blobs into different groups of continuous + * y position. jumplimit specifies the max allowable limit on a jump + * before a new partition is started. + * The return value is the biggest partition + **********************************************************************/ + +int +partition_line ( //partition blobs +BOX blobcoords[], //bounding boxes +int blobcount, /*no of blobs on row */ +int *numparts, /*number of partitions */ +char partids[], /*partition no of each blob */ +int partsizes[], /*no in each partition */ +QSPLINE * spline, /*curve to fit to */ +float jumplimit, /*allowed delta change */ +float ydiffs[] /*diff from spline */ +) { + register int blobindex; /*no along text line */ + int bestpart; /*best new partition */ + int biggestpart; /*part with most members */ + float diff; /*difference from line */ + int startx; /*index of start blob */ + float partdiffs[MAXPARTS]; /*step between parts */ + + for (bestpart = 0; bestpart < MAXPARTS; bestpart++) + partsizes[bestpart] = 0; /*zero them all */ + + startx = get_ydiffs (blobcoords, blobcount, spline, ydiffs); + *numparts = 1; /*1 partition */ + bestpart = -1; /*first point */ + for (blobindex = startx; blobindex < blobcount; blobindex++) { + /*do each blob in row */ + diff = ydiffs[blobindex]; /*diff from line */ + if (textord_oldbl_debug) { + tprintf ("%d(%d,%d), ", blobindex, + blobcoords[blobindex].left (), + blobcoords[blobindex].bottom ()); + } + bestpart = + choose_partition(diff, partdiffs, bestpart, jumplimit, numparts); + /*record partition */ + partids[blobindex] = bestpart; + partsizes[bestpart]++; /*another in it */ + } + + bestpart = -1; /*first point */ + partsizes[0]--; /*doing 1st pt again */ + /*do each blob in row */ + for (blobindex = startx; blobindex >= 0; blobindex--) { + diff = ydiffs[blobindex]; /*diff from line */ + if (textord_oldbl_debug) { + tprintf ("%d(%d,%d), ", blobindex, + blobcoords[blobindex].left (), + blobcoords[blobindex].bottom ()); + } + bestpart = + choose_partition(diff, partdiffs, bestpart, jumplimit, numparts); + /*record partition */ + partids[blobindex] = bestpart; + partsizes[bestpart]++; /*another in it */ + } + + for (biggestpart = 0, bestpart = 1; bestpart < *numparts; bestpart++) + if (partsizes[bestpart] >= partsizes[biggestpart]) + biggestpart = bestpart; /*new biggest */ + if (textord_oldbl_merge_parts) + merge_oldbl_parts(blobcoords, + blobcount, + partids, + partsizes, + biggestpart, + jumplimit); + return biggestpart; /*biggest partition */ +} + + +/********************************************************************** + * merge_oldbl_parts + * + * For any adjacent group of blobs in a different part, put them in the + * main part if they fit closely to neighbours in the main part. + **********************************************************************/ + +void +merge_oldbl_parts ( //partition blobs +BOX blobcoords[], //bounding boxes +int blobcount, /*no of blobs on row */ +char partids[], /*partition no of each blob */ +int partsizes[], /*no in each partition */ +int biggestpart, //major partition +float jumplimit /*allowed delta change */ +) { + BOOL8 found_one; //found a bestpart blob + BOOL8 close_one; //found was close enough + register int blobindex; /*no along text line */ + int prevpart; //previous iteration + int runlength; //no in this part + float diff; /*difference from line */ + int startx; /*index of start blob */ + int test_blob; //another index + FCOORD coord; //blob coordinate + float m, c; //fitted line + QLSQ stats; //line stuff + + prevpart = biggestpart; + runlength = 0; + startx = 0; + for (blobindex = 0; blobindex < blobcount; blobindex++) { + if (partids[blobindex] != prevpart) { + // tprintf("Partition change at (%d,%d) from %d to %d after run of %d\n", + // blobcoords[blobindex].left(),blobcoords[blobindex].bottom(), + // prevpart,partids[blobindex],runlength); + if (prevpart != biggestpart && runlength > MAXBADRUN) { + stats.clear (); + for (test_blob = startx; test_blob < blobindex; test_blob++) { + coord = FCOORD ((blobcoords[test_blob].left () + + blobcoords[test_blob].right ()) / 2.0, + blobcoords[test_blob].bottom ()); + stats.add (coord.x (), coord.y ()); + } + stats.fit (1); + m = stats.get_b (); + c = stats.get_c (); + if (textord_oldbl_debug) + tprintf ("Fitted line y=%g x + %g\n", m, c); + found_one = FALSE; + close_one = FALSE; + for (test_blob = 1; !found_one + && (startx - test_blob >= 0 + || blobindex + test_blob <= blobcount); test_blob++) { + if (startx - test_blob >= 0 + && partids[startx - test_blob] == biggestpart) { + found_one = TRUE; + coord = FCOORD ((blobcoords[startx - test_blob].left () + + blobcoords[startx - + test_blob].right ()) / + 2.0, + blobcoords[startx - + test_blob].bottom ()); + diff = m * coord.x () + c - coord.y (); + if (textord_oldbl_debug) + tprintf + ("Diff of common blob to suspect part=%g at (%g,%g)\n", + diff, coord.x (), coord.y ()); + if (diff < jumplimit && -diff < jumplimit) + close_one = TRUE; + } + if (blobindex + test_blob <= blobcount + && partids[blobindex + test_blob - 1] == biggestpart) { + found_one = TRUE; + coord = + FCOORD ((blobcoords[blobindex + test_blob - 1]. + left () + blobcoords[blobindex + test_blob - + 1].right ()) / 2.0, + blobcoords[blobindex + test_blob - + 1].bottom ()); + diff = m * coord.x () + c - coord.y (); + if (textord_oldbl_debug) + tprintf + ("Diff of common blob to suspect part=%g at (%g,%g)\n", + diff, coord.x (), coord.y ()); + if (diff < jumplimit && -diff < jumplimit) + close_one = TRUE; + } + } + if (close_one) { + if (textord_oldbl_debug) + tprintf + ("Merged %d blobs back into part %d from %d starting at (%d,%d)\n", + runlength, biggestpart, prevpart, + blobcoords[startx].left (), + blobcoords[startx].bottom ()); + //switch sides + partsizes[prevpart] -= runlength; + for (test_blob = startx; test_blob < blobindex; test_blob++) + partids[test_blob] = biggestpart; + } + } + prevpart = partids[blobindex]; + runlength = 1; + startx = blobindex; + } + else + runlength++; + } +} + + +/********************************************************************** + * get_ydiffs + * + * Get the differences between the blobs and the spline, + * putting them in ydiffs. The return value is the index + * of the blob in the middle of the "best behaved" region + **********************************************************************/ + +int +get_ydiffs ( //evaluate differences +BOX blobcoords[], //bounding boxes +int blobcount, /*no of blobs */ +QSPLINE * spline, /*approximating spline */ +float ydiffs[] /*output */ +) { + register int blobindex; /*current blob */ + int xcentre; /*xcoord */ + int lastx; /*last xcentre */ + float diffsum; /*sum of diffs */ + float diff; /*current difference */ + float drift; /*sum of spline steps */ + float bestsum; /*smallest diffsum */ + int bestindex; /*index of bestsum */ + + diffsum = 0.0f; + bestindex = 0; + bestsum = (float) MAX_INT32; + drift = 0.0f; + lastx = blobcoords[0].left (); + /*do each blob in row */ + for (blobindex = 0; blobindex < blobcount; blobindex++) { + /*centre of blob */ + xcentre = (blobcoords[blobindex].left () + blobcoords[blobindex].right ()) >> 1; + //step functions in spline + drift += spline->step (lastx, xcentre); + lastx = xcentre; + diff = blobcoords[blobindex].bottom (); + diff -= spline->y (xcentre); + diff += drift; + ydiffs[blobindex] = diff; /*store difference */ + if (blobindex > 2) + /*remove old one */ + diffsum -= ABS (ydiffs[blobindex - 3]); + diffsum += ABS (diff); /*add new one */ + if (blobindex >= 2 && diffsum < bestsum) { + bestsum = diffsum; /*find min sum */ + bestindex = blobindex - 1; /*middle of set */ + } + } + return bestindex; +} + + +/********************************************************************** + * choose_partition + * + * Choose a partition for the point and return the index. + **********************************************************************/ + +int +choose_partition ( //select partition +register float diff, /*diff from spline */ +float partdiffs[], /*diff on all parts */ +int lastpart, /*last assigned partition */ +float jumplimit, /*new part threshold */ +int *partcount /*no of partitions */ +) { + register int partition; /*partition no */ + int bestpart; /*best new partition */ + float bestdelta; /*best gap from a part */ + static float drift; /*drift from spline */ + float delta; /*diff from part */ + static float lastdelta; /*previous delta */ + + if (lastpart < 0) { + partdiffs[0] = diff; + lastpart = 0; /*first point */ + drift = 0.0f; + lastdelta = 0.0f; + } + /*adjusted diff from part */ + delta = diff - partdiffs[lastpart] - drift; + if (textord_oldbl_debug) { + tprintf ("Diff=%.2f, Delta=%.3f, Drift=%.3f, ", diff, delta, drift); + } + if (ABS (delta) > jumplimit / 2) { + /*delta on part 0 */ + bestdelta = diff - partdiffs[0] - drift; + bestpart = 0; /*0 best so far */ + for (partition = 1; partition < *partcount; partition++) { + delta = diff - partdiffs[partition] - drift; + if (ABS (delta) < ABS (bestdelta)) { + bestdelta = delta; + bestpart = partition; /*part with nearest jump */ + } + } + delta = bestdelta; + /*too far away */ + if (ABS (bestdelta) > jumplimit + && *partcount < MAXPARTS) { /*and spare part left */ + bestpart = (*partcount)++; /*best was new one */ + /*start new one */ + partdiffs[bestpart] = diff - drift; + delta = 0.0f; + } + } + else { + bestpart = lastpart; /*best was last one */ + } + + if (bestpart == lastpart + && (ABS (delta - lastdelta) < jumplimit / 2 + || ABS (delta) < jumplimit / 2)) + /*smooth the drift */ + drift = (3 * drift + delta) / 3; + lastdelta = delta; + + if (textord_oldbl_debug) { + tprintf ("P=%d\n", bestpart); + } + + return bestpart; +} + + +///*merge_partitions(partids,partcount,blobcount,bestpart) discards funny looking +//partitions and gives all the rest partid 0*/ +// +//merge_partitions(partids,partcount,blobcount,bestpart) +//register char *partids; /*partition numbers*/ +//int partcount; /*no of partitions*/ +//int blobcount; /*no of blobs*/ +//int bestpart; /*best partition*/ +//{ +// register int blobindex; /*no along text line*/ +// int runlength; /*run of same partition*/ +// int bestrun; /*biggest runlength*/ +// +// bestrun=0; /*no runs yet*/ +// runlength=1; +// for (blobindex=1;blobindexbestrun) +// bestrun=runlength; /*find biggest run*/ +// runlength=1; /*new run*/ +// } +// else +// { runlength++; +// } +// } +// if (runlength>bestrun) +// bestrun=runlength; +// +// for (blobindex=0;blobindex=blobcount +// || partids[blobindex]!=partids[blobindex+1]) +// /*loner*/ +// && (bestrun>2 || partids[blobindex]!=bestpart)) +// { partids[blobindex]=partcount; /*discard loner*/ +// } +// else if (blobindex+1=blobcount +// || partids[blobindex]!=partids[blobindex+2]) +// && (bestrun>3 || partids[blobindex]!=bestpart)) +// { partids[blobindex]=partcount; /*discard both*/ +// partids[blobindex+1]=partcount; +// } +// } +// } +// for (blobindex=0;blobindex> 1; + ycoords[pointcount++] = blobcoords[blobindex].bottom (); + } + } + return pointcount; /*no of points found */ +} + + +/********************************************************************** + * segment_spline + * + * Segment the row at midpoints between maxima and minima of the x,y pairs. + * The xstarts of the segments are returned and the number found. + **********************************************************************/ + +int +segment_spline ( //make xstarts +BOX blobcoords[], //boundign boxes +int blobcount, /*no of blobs in row */ +int xcoords[], /*points to work on */ +int ycoords[], /*points to work on */ +int degree, int pointcount, /*no of points */ +int xstarts[] //result +) { + register int ptindex; /*no along text line */ + register int segment; /*partition no */ + int lastmin, lastmax; /*possible turn points */ + int turnpoints[SPLINESIZE]; /*good turning points */ + int turncount; /*no of turning points */ + int max_x; //max specified coord + + xstarts[0] = xcoords[0] - 1; //leftmost defined pt + max_x = xcoords[pointcount - 1] + 1; + if (degree < 2) + pointcount = 0; + turncount = 0; /*no turning points yet */ + if (pointcount > 3) { + ptindex = 1; + lastmax = lastmin = 0; /*start with first one */ + while (ptindex < pointcount - 1 && turncount < SPLINESIZE - 1) { + /*minimum */ + if (ycoords[ptindex - 1] > ycoords[ptindex] && ycoords[ptindex] <= ycoords[ptindex + 1]) { + if (ycoords[ptindex] < ycoords[lastmax] - TURNLIMIT) { + if (turncount == 0 || turnpoints[turncount - 1] != lastmax) + /*new max point */ + turnpoints[turncount++] = lastmax; + lastmin = ptindex; /*latest minimum */ + } + else if (ycoords[ptindex] < ycoords[lastmin]) { + lastmin = ptindex; /*lower minimum */ + } + } + + /*maximum */ + if (ycoords[ptindex - 1] < ycoords[ptindex] && ycoords[ptindex] >= ycoords[ptindex + 1]) { + if (ycoords[ptindex] > ycoords[lastmin] + TURNLIMIT) { + if (turncount == 0 || turnpoints[turncount - 1] != lastmin) + /*new min point */ + turnpoints[turncount++] = lastmin; + lastmax = ptindex; /*latest maximum */ + } + else if (ycoords[ptindex] > ycoords[lastmax]) { + lastmax = ptindex; /*higher maximum */ + } + } + ptindex++; + } + /*possible global min */ + if (ycoords[ptindex] < ycoords[lastmax] - TURNLIMIT + && (turncount == 0 || turnpoints[turncount - 1] != lastmax)) { + if (turncount < SPLINESIZE - 1) + /*2 more turns */ + turnpoints[turncount++] = lastmax; + if (turncount < SPLINESIZE - 1) + turnpoints[turncount++] = ptindex; + } + else if (ycoords[ptindex] > ycoords[lastmin] + TURNLIMIT + /*possible global max */ + && (turncount == 0 || turnpoints[turncount - 1] != lastmin)) { + if (turncount < SPLINESIZE - 1) + /*2 more turns */ + turnpoints[turncount++] = lastmin; + if (turncount < SPLINESIZE - 1) + turnpoints[turncount++] = ptindex; + } + else if (turncount > 0 && turnpoints[turncount - 1] == lastmin + && turncount < SPLINESIZE - 1) { + if (ycoords[ptindex] > ycoords[lastmax]) + turnpoints[turncount++] = ptindex; + else + turnpoints[turncount++] = lastmax; + } + else if (turncount > 0 && turnpoints[turncount - 1] == lastmax + && turncount < SPLINESIZE - 1) { + if (ycoords[ptindex] < ycoords[lastmin]) + turnpoints[turncount++] = ptindex; + else + turnpoints[turncount++] = lastmin; + } + } + + if (textord_oldbl_debug && turncount > 0) + tprintf ("First turn is %d at (%d,%d)\n", + turnpoints[0], xcoords[turnpoints[0]], ycoords[turnpoints[0]]); + for (segment = 1; segment < turncount; segment++) { + /*centre y coord */ + lastmax = (ycoords[turnpoints[segment - 1]] + ycoords[turnpoints[segment]]) / 2; + + /* fix alg so that it works with both rising and falling sections */ + if (ycoords[turnpoints[segment - 1]] < ycoords[turnpoints[segment]]) + /*find rising y centre */ + for (ptindex = turnpoints[segment - 1] + 1; ptindex < turnpoints[segment] && ycoords[ptindex + 1] <= lastmax; ptindex++); + else + /*find falling y centre */ + for (ptindex = turnpoints[segment - 1] + 1; ptindex < turnpoints[segment] && ycoords[ptindex + 1] >= lastmax; ptindex++); + + /*centre x */ + xstarts[segment] = (xcoords[ptindex - 1] + xcoords[ptindex] + + xcoords[turnpoints[segment - 1]] + + xcoords[turnpoints[segment]] + 2) / 4; + /*halfway between turns */ + if (textord_oldbl_debug) + tprintf ("Turn %d is %d at (%d,%d), mid pt is %d@%d, final @%d\n", + segment, turnpoints[segment], + xcoords[turnpoints[segment]], ycoords[turnpoints[segment]], + ptindex - 1, xcoords[ptindex - 1], xstarts[segment]); + } + + xstarts[segment] = max_x; + return segment; /*no of splines */ +} + + +/********************************************************************** + * split_stepped_spline + * + * Re-segment the spline in cases where there is a big step function. + * Return TRUE if any were done. + **********************************************************************/ + +BOOL8 +split_stepped_spline ( //make xstarts +QSPLINE * baseline, //current shot +float jumplimit, //max step fuction +int xcoords[], /*points to work on */ +int xstarts[], //result +int &segments //no of segments +) { + BOOL8 doneany; //return value + register int segment; /*partition no */ + int startindex, centreindex, endindex; + float leftcoord, rightcoord; + int leftindex, rightindex; + float step; //spline step + + doneany = FALSE; + startindex = 0; + for (segment = 1; segment < segments - 1; segment++) { + step = baseline->step ((xstarts[segment - 1] + xstarts[segment]) / 2.0, + (xstarts[segment] + xstarts[segment + 1]) / 2.0); + if (step < 0) + step = -step; + if (step > jumplimit) { + while (xcoords[startindex] < xstarts[segment - 1]) + startindex++; + centreindex = startindex; + while (xcoords[centreindex] < xstarts[segment]) + centreindex++; + endindex = centreindex; + while (xcoords[endindex] < xstarts[segment + 1]) + endindex++; + if (segments >= SPLINESIZE) { + if (textord_debug_baselines) + tprintf ("Too many segments to resegment spline!!\n"); + } + else if (endindex - startindex >= textord_spline_medianwin * 3) { + while (centreindex - startindex < + textord_spline_medianwin * 3 / 2) + centreindex++; + while (endindex - centreindex < + textord_spline_medianwin * 3 / 2) + centreindex--; + leftindex = (startindex + startindex + centreindex) / 3; + rightindex = (centreindex + endindex + endindex) / 3; + leftcoord = + (xcoords[startindex] * 2 + xcoords[centreindex]) / 3.0; + rightcoord = + (xcoords[centreindex] + xcoords[endindex] * 2) / 3.0; + while (xcoords[leftindex] > leftcoord + && leftindex - startindex > textord_spline_medianwin) + leftindex--; + while (xcoords[leftindex] < leftcoord + && centreindex - leftindex > + textord_spline_medianwin / 2) + leftindex++; + if (xcoords[leftindex] - leftcoord > + leftcoord - xcoords[leftindex - 1]) + leftindex--; + while (xcoords[rightindex] > rightcoord + && rightindex - centreindex > + textord_spline_medianwin / 2) + rightindex--; + while (xcoords[rightindex] < rightcoord + && endindex - rightindex > textord_spline_medianwin) + rightindex++; + if (xcoords[rightindex] - rightcoord > + rightcoord - xcoords[rightindex - 1]) + rightindex--; + if (textord_debug_baselines) + tprintf ("Splitting spline at %d with step %g at (%d,%d)\n", + xstarts[segment], + baseline-> + step ((xstarts[segment - 1] + + xstarts[segment]) / 2.0, + (xstarts[segment] + + xstarts[segment + 1]) / 2.0), + (xcoords[leftindex - 1] + xcoords[leftindex]) / 2, + (xcoords[rightindex - 1] + xcoords[rightindex]) / 2); + insert_spline_point (xstarts, segment, + (xcoords[leftindex - 1] + + xcoords[leftindex]) / 2, + (xcoords[rightindex - 1] + + xcoords[rightindex]) / 2, segments); + doneany = TRUE; + } + else if (textord_debug_baselines) { + tprintf + ("Resegmenting spline failed - insufficient pts (%d,%d,%d,%d)\n", + startindex, centreindex, endindex, + (INT32) textord_spline_medianwin); + } + } + // else tprintf("Spline step at %d is %g\n", + // xstarts[segment], + // baseline->step((xstarts[segment-1]+xstarts[segment])/2.0, + // (xstarts[segment]+xstarts[segment+1])/2.0)); + } + return doneany; +} + + +/********************************************************************** + * insert_spline_point + * + * Insert a new spline point and shuffle up the others. + **********************************************************************/ + +void +insert_spline_point ( //get descenders +int xstarts[], //starts to shuffle +int segment, //insertion pt +int coord1, //coords to add +int coord2, int &segments //total segments +) { + int index; //for shuffling + + for (index = segments; index > segment; index--) + xstarts[index + 1] = xstarts[index]; + segments++; + xstarts[segment] = coord1; + xstarts[segment + 1] = coord2; +} + + +/********************************************************************** + * find_lesser_parts + * + * Average the step from the spline for the other partitions + * and find the commonest partition which has a descender. + **********************************************************************/ + +void +find_lesser_parts ( //get descenders +TO_ROW * row, //row to process +BOX blobcoords[], //bounding boxes +int blobcount, /*no of blobs */ +char partids[], /*partition of each blob */ +int partsizes[], /*size of each part */ +int partcount, /*no of partitions */ +int bestpart /*biggest partition */ +) { + register int blobindex; /*index of blob */ + register int partition; /*current partition */ + int xcentre; /*centre of blob */ + int poscount; /*count of best up step */ + int negcount; /*count of best down step */ + float partsteps[MAXPARTS]; /*average step to part */ + float bestpos; /*best up step */ + float bestneg; /*best down step */ + int runlength; /*length of bad run */ + int biggestrun; /*biggest bad run */ + + biggestrun = 0; + for (partition = 0; partition < partcount; partition++) + partsteps[partition] = 0.0; /*zero accumulators */ + for (runlength = 0, blobindex = 0; blobindex < blobcount; blobindex++) { + xcentre = (blobcoords[blobindex].left () + + blobcoords[blobindex].right ()) >> 1; + /*in other parts */ + if (partids[blobindex] != bestpart) { + runlength++; /*run of non bests */ + if (runlength > biggestrun) + biggestrun = runlength; + partsteps[partids[blobindex]] += blobcoords[blobindex].bottom () + - row->baseline.y (xcentre); + } + else + runlength = 0; + } + if (biggestrun > MAXBADRUN) + row->xheight = -1.0f; /*failed */ + else + row->xheight = 1.0f; /*success */ + poscount = negcount = 0; + bestpos = bestneg = 0.0; /*no step yet */ + for (partition = 0; partition < partcount; partition++) { + if (partition != bestpart) { + partsteps[partition] /= partsizes[partition]; + if (partsteps[partition] >= MINASCRISE + && partsizes[partition] > poscount) { + /*ascender rise */ + bestpos = partsteps[partition]; + /*2nd most popular */ + poscount = partsizes[partition]; + } + if (partsteps[partition] <= -MINASCRISE + && partsizes[partition] > negcount) { + /*ascender rise */ + bestneg = partsteps[partition]; + /*2nd most popular */ + negcount = partsizes[partition]; + } + } + } + /*average x-height */ + partsteps[bestpart] /= blobcount; + row->descdrop = bestneg; +} + + +/********************************************************************** + * old_first_xheight + * + * Makes an x-height spline by copying the baseline and shifting it. + * It estimates the x-height across the line to use as the shift. + * It also finds the ascender height if it can. + **********************************************************************/ + +void +old_first_xheight ( //the wiseowl way +TO_ROW * row, /*current row */ +BOX blobcoords[], /*blob bounding boxes */ +int initialheight, //initial guess +int blobcount, /*blobs in blobcoords */ +QSPLINE * baseline, /*established */ +float jumplimit /*min ascender height */ +) { + register int blobindex; /*current blob */ + /*height statistics */ + STATS heightstat (0, MAXHEIGHT); + int height; /*height of blob */ + int xcentre; /*centre of blob */ + int lineheight; /*approx xheight */ + float ascenders; /*ascender sum */ + int asccount; /*no of ascenders */ + float xsum; /*xheight sum */ + int xcount; /*xheight count */ + register float diff; /*height difference */ + + if (blobcount > 1) { + for (blobindex = 0; blobindex < blobcount; blobindex++) { + xcentre = (blobcoords[blobindex].left () + + blobcoords[blobindex].right ()) / 2; + /*height of blob */ + height = (int) (blobcoords[blobindex].top () - baseline->y (xcentre) + 0.5); + if (height > initialheight * oldbl_xhfract + && height > textord_min_xheight) + heightstat.add (height, 1); + } + if (heightstat.get_total () > 3) { + lineheight = (int) heightstat.ile (0.25); + if (lineheight <= 0) + lineheight = (int) heightstat.ile (0.5); + } + else + lineheight = initialheight; + } + else { + lineheight = (int) (blobcoords[0].top () + - baseline->y ((blobcoords[0].left () + + blobcoords[0].right ()) / 2) + + 0.5); + } + + xsum = 0.0f; + xcount = 0; + for (ascenders = 0.0f, asccount = 0, blobindex = 0; blobindex < blobcount; + blobindex++) { + xcentre = (blobcoords[blobindex].left () + + blobcoords[blobindex].right ()) / 2; + diff = blobcoords[blobindex].top () - baseline->y (xcentre); + /*is it ascender */ + if (diff > lineheight + jumplimit) { + ascenders += diff; + asccount++; /*count ascenders */ + } + else if (diff > lineheight - jumplimit) { + xsum += diff; /*mean xheight */ + xcount++; + } + } + if (xcount > 0) + xsum /= xcount; /*average xheight */ + else + xsum = (float) lineheight; /*guess it */ + row->xheight *= xsum; + if (asccount > 0) + row->ascrise = ascenders / asccount - xsum; + else + row->ascrise = 0.0f; /*had none */ + if (row->xheight == 0) + row->xheight = -1.0f; +} + + +/********************************************************************** + * make_first_xheight + * + * Makes an x-height spline by copying the baseline and shifting it. + * It estimates the x-height across the line to use as the shift. + * It also finds the ascender height if it can. + **********************************************************************/ + +void +make_first_xheight ( //find xheight +TO_ROW * row, /*current row */ +BOX blobcoords[], /*blob bounding boxes */ +int lineheight, //initial guess +int init_lineheight, //block level guess +int blobcount, /*blobs in blobcoords */ +QSPLINE * baseline, /*established */ +float jumplimit /*min ascender height */ +) { + int *heights; + STATS heightstat (0, HEIGHTBUCKETS); + int modelist[MODENUM]; + int blobindex; + int mode_count; //blobs to count in thr + int sign_bit; + int mode_threshold; + + sign_bit = row->xheight > 0 ? 1 : -1; + heights = make_height_array (blobcoords, blobcount, baseline); + + mode_count = 0; + for (blobindex = 0; blobindex < blobcount; blobindex++) { + if (heights[blobindex] > lineheight * oldbl_xhfract + && blobcoords[blobindex].height () > init_lineheight * 0.25 + && heights[blobindex] > textord_min_xheight) + heightstat.add (heights[blobindex], 1); + if (blobcoords[blobindex].height () > init_lineheight * 0.25) + mode_count++; + } + + mode_threshold = (int) (blobcount * 0.1); + if (oldbl_dot_error_size > 1 || oldbl_xhfix) + mode_threshold = (int) (mode_count * 0.1); + + if (textord_oldbl_debug) { + tprintf ("blobcount=%d, mode_count=%d, mode_t=%d\n", + blobcount, mode_count, mode_threshold); + } + find_top_modes(&heightstat, HEIGHTBUCKETS, modelist, MODENUM); + if (textord_oldbl_debug) { + for (blobindex = 0; blobindex < MODENUM; blobindex++) + tprintf ("mode[%d]=%d ", blobindex, modelist[blobindex]); + tprintf ("\n"); + } + pick_x_height(row, modelist, &heightstat, mode_threshold); + + if (textord_oldbl_debug) + tprintf ("Output xheight=%g\n", row->xheight); + if (row->xheight < 0 && textord_oldbl_debug) + tprintf ("warning: Row Line height < 0; %4.2f\n", row->xheight); + + free_mem(heights); + + if (sign_bit < 0) + row->xheight = -row->xheight; +} + + +/********************************************************************** + * make_height_array + * + * Create an array of the number of blobs and each of their heights. + **********************************************************************/ + +int * +make_height_array ( //get array of heights +BOX blobcoords[], /*blob bounding boxes */ +int blobcount, /*blobs in blobcoords */ +QSPLINE * baseline /*established */ +) { + int blobindex; + int xcenter; + int *heights; + + heights = (int *) alloc_mem (sizeof (int) * blobcount); + + for (blobindex = 0; blobindex < blobcount; blobindex++) { + xcenter = (blobcoords[blobindex].left () + + blobcoords[blobindex].right ()) / 2; + heights[blobindex] = (int) (blobcoords[blobindex].top () - + baseline->y (xcenter) + 0.5); + } + + return (heights); +} + + +/********************************************************************** + * find_top_modes + * + * Fill the input array with the indices of the top ten modes of the + * input distribution. + **********************************************************************/ + +const int kMinModeFactor = 12; + +void +find_top_modes ( //get modes +STATS * stats, //stats to hack +int statnum, //no of piles +int modelist[], int modenum //no of modes to get +) { + int mode_count; + int last_i = 0; + int last_max = MAX_INT32; + int i; + int mode; + int total_max = 0; + + for (mode_count = 0; mode_count < modenum; mode_count++) { + mode = 0; + for (i = 0; i < statnum; i++) { + if (stats->pile_count (i) > stats->pile_count (mode)) { + if ((stats->pile_count (i) < last_max) || + ((stats->pile_count (i) == last_max) && (i > last_i))) { + mode = i; + } + } + } + last_i = mode; + last_max = stats->pile_count (last_i); + total_max += last_max; + if (last_max <= total_max / kMinModeFactor) + mode = 0; + modelist[mode_count] = mode; + } +} + + +/********************************************************************** + * pick_x_height + * + * Choose based on the height modes the best x height value. + **********************************************************************/ + +void +pick_x_height ( //find xheight +TO_ROW * row, //row to do + //height stats +int modelist[], STATS * heightstat, +int mode_threshold) { + int x; + int y; + int z; + float ratio; + int found_one_bigger = FALSE; + int best_x_height = 0; + int best_asc = 0; + int num_in_best; + + for (x = 0; x < MODENUM; x++) { + for (y = 0; y < MODENUM; y++) { + /* Check for two modes */ + if (modelist[x] && modelist[y] && + heightstat->pile_count (modelist[x]) > mode_threshold) { + ratio = (float) modelist[y] / (float) modelist[x]; + if (1.2 < ratio && ratio < 1.8) { + if (modelist[y] && modelist[x]) { + /* Two modes found */ + best_x_height = modelist[x]; + num_in_best = heightstat->pile_count (modelist[x]); + + /* Try to get one higher */ + do { + found_one_bigger = FALSE; + for (z = 0; z < MODENUM; z++) { + if (modelist[z] == best_x_height + 1) { + ratio = + (float) modelist[y] / (float) modelist[z]; + if ((1.2 < ratio && ratio < 1.8) && + /* Should be half of best */ + heightstat->pile_count (modelist[z]) > + num_in_best * 0.5) { + best_x_height++; + found_one_bigger = TRUE; + break; + } + } + } + } + while (found_one_bigger); + + /* try to get a higher ascender */ + + best_asc = modelist[y]; + num_in_best = heightstat->pile_count (modelist[y]); + + /* Try to get one higher */ + do { + found_one_bigger = FALSE; + for (z = 0; z < MODENUM; z++) { + if (modelist[z] > best_asc) { + ratio = + (float) modelist[z] / + (float) best_x_height; + if ((1.2 < ratio && ratio < 1.8) && + /* Should be half of best */ + heightstat->pile_count (modelist[z]) > + num_in_best * 0.5) { + best_asc = modelist[z]; + found_one_bigger = TRUE; + break; + } + } + } + } + while (found_one_bigger); + + row->xheight = (float) best_x_height; + row->ascrise = (float) best_asc - best_x_height; + return; + } + } + } + } + } + + best_x_height = modelist[0]; /* Single Mode found */ + num_in_best = heightstat->pile_count (best_x_height); + do { + /* Try to get one higher */ + found_one_bigger = FALSE; + for (z = 1; z < MODENUM; z++) { + /* Should be half of best */ + if ((modelist[z] == best_x_height + 1) && + (heightstat->pile_count (modelist[z]) > num_in_best * 0.5)) { + best_x_height++; + found_one_bigger = TRUE; + break; + } + } + } + while (found_one_bigger); + + row->ascrise = 0.0f; + row->xheight = (float) best_x_height; + if (row->xheight == 0) + row->xheight = -1.0f; +} diff --git a/textord/oldbasel.h b/textord/oldbasel.h new file mode 100644 index 0000000000..e87a3219fa --- /dev/null +++ b/textord/oldbasel.h @@ -0,0 +1,195 @@ +/********************************************************************** + * File: oldbasel.h (Formerly oldbl.h) + * Description: A re-implementation of the old baseline algorithm. + * Author: Ray Smith + * Created: Wed Oct 6 09:41:48 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef OLDBASEL_H +#define OLDBASEL_H + +#include "varable.h" +#include "blobbox.h" +#include "notdll.h" +extern BOOL_VAR_H (textord_really_old_xheight, FALSE, +"Use original wiseowl xheight"); +extern BOOL_VAR_H (textord_oldbl_debug, FALSE, +"Debug old baseline generation"); +extern BOOL_VAR_H (textord_debug_baselines, FALSE, +"Debug baseline generation"); +extern BOOL_VAR_H (textord_oldbl_paradef, TRUE, "Use para default mechanism"); +extern BOOL_VAR_H (textord_oldbl_split_splines, TRUE, +"Split stepped splines"); +extern BOOL_VAR_H (textord_oldbl_merge_parts, TRUE, +"Merge suspect partitions"); +extern BOOL_VAR_H (oldbl_xhfix, FALSE, +"Fix bug in modes threshold for xheights"); +extern INT_VAR_H (oldbl_holed_losscount, 10, +"Max lost before fallback line used"); +extern double_VAR_H (oldbl_dot_error_size, 1.26, "Max aspect ratio of a dot"); +extern double_VAR_H (textord_oldbl_jumplimit, 0.15, +"X fraction for new partition"); +void make_old_baselines( //make splines + TO_BLOCK *block, //block to do + BOOL8 testing_on //correct orientation + ); +void correlate_lines( //cleanup lines + TO_BLOCK *block //block to do + ); +void correlate_neighbours( //fix bad rows + TO_BLOCK *block, /*block rows are in */ + TO_ROW **rows, /*rows of block */ + int rowcount /*no of rows to do */ + ); +int correlate_with_stats( //fix xheights + TO_ROW **rows, /*rows of block */ + int rowcount /*no of rows to do */ + ); +void find_textlines( //get baseline + TO_BLOCK *block, //block row is in + TO_ROW *row, //row to do + int degree, //required approximation + QSPLINE *spline //starting spline + ); +int get_blob_coords( //get boxes + TO_ROW *row, //row to use + INT32 lineheight, //block level + BOX *blobcoords, //ouput boxes + BOOL8 &holed_line, //lost a lot of blobs + int &outcount //no of real blobs + ); +void make_first_baseline ( //initial approximation +BOX blobcoords[], /*blob bounding boxes */ +int blobcount, /*no of blobcoords */ +int xcoords[], /*coords for spline */ +int ycoords[], /*approximator */ +QSPLINE * spline, /*initial spline */ +QSPLINE * baseline, /*output spline */ +float jumplimit /*guess half descenders */ +); +void make_holed_baseline ( //initial approximation +BOX blobcoords[], /*blob bounding boxes */ +int blobcount, /*no of blobcoords */ +QSPLINE * spline, /*initial spline */ +QSPLINE * baseline, /*output spline */ +float gradient //of line +); +int partition_line ( //partition blobs +BOX blobcoords[], //bounding boxes +int blobcount, /*no of blobs on row */ +int *numparts, /*number of partitions */ +char partids[], /*partition no of each blob */ +int partsizes[], /*no in each partition */ +QSPLINE * spline, /*curve to fit to */ +float jumplimit, /*allowed delta change */ +float ydiffs[] /*diff from spline */ +); +void merge_oldbl_parts ( //partition blobs +BOX blobcoords[], //bounding boxes +int blobcount, /*no of blobs on row */ +char partids[], /*partition no of each blob */ +int partsizes[], /*no in each partition */ +int biggestpart, //major partition +float jumplimit /*allowed delta change */ +); +int get_ydiffs ( //evaluate differences +BOX blobcoords[], //bounding boxes +int blobcount, /*no of blobs */ +QSPLINE * spline, /*approximating spline */ +float ydiffs[] /*output */ +); +int choose_partition ( //select partition +register float diff, /*diff from spline */ +float partdiffs[], /*diff on all parts */ +int lastpart, /*last assigned partition */ +float jumplimit, /*new part threshold */ +int *partcount /*no of partitions */ +); +int partition_coords ( //find relevant coords +BOX blobcoords[], //bounding boxes +int blobcount, /*no of blobs in row */ +char partids[], /*partition no of each blob */ +int bestpart, /*best new partition */ +int xcoords[], /*points to work on */ +int ycoords[] /*points to work on */ +); +int segment_spline ( //make xstarts +BOX blobcoords[], //boundign boxes +int blobcount, /*no of blobs in row */ +int xcoords[], /*points to work on */ +int ycoords[], /*points to work on */ +int degree, int pointcount, /*no of points */ +int xstarts[] //result +); +BOOL8 split_stepped_spline ( //make xstarts +QSPLINE * baseline, //current shot +float jumplimit, //max step fuction +int xcoords[], /*points to work on */ +int xstarts[], //result +int &segments //no of segments +); +void insert_spline_point ( //get descenders +int xstarts[], //starts to shuffle +int segment, //insertion pt +int coord1, //coords to add +int coord2, int &segments //total segments +); +void find_lesser_parts ( //get descenders +TO_ROW * row, //row to process +BOX blobcoords[], //bounding boxes +int blobcount, /*no of blobs */ +char partids[], /*partition of each blob */ +int partsizes[], /*size of each part */ +int partcount, /*no of partitions */ +int bestpart /*biggest partition */ +); + +void old_first_xheight ( //the wiseowl way +TO_ROW * row, /*current row */ +BOX blobcoords[], /*blob bounding boxes */ +int initialheight, //initial guess +int blobcount, /*blobs in blobcoords */ +QSPLINE * baseline, /*established */ +float jumplimit /*min ascender height */ +); + +void make_first_xheight ( //find xheight +TO_ROW * row, /*current row */ +BOX blobcoords[], /*blob bounding boxes */ +int lineheight, //initial guess +int init_lineheight, //block level guess +int blobcount, /*blobs in blobcoords */ +QSPLINE * baseline, /*established */ +float jumplimit /*min ascender height */ +); + +int *make_height_array ( //get array of heights +BOX blobcoords[], /*blob bounding boxes */ +int blobcount, /*blobs in blobcoords */ +QSPLINE * baseline /*established */ +); + +void find_top_modes ( //get modes +STATS * stats, //stats to hack +int statnum, //no of piles +int modelist[], int modenum //no of modes to get +); + +void pick_x_height ( //find xheight +TO_ROW * row, //row to do + //height stats +int modelist[], STATS * heightstat, +int mode_threshold); +#endif diff --git a/textord/pithsync.cpp b/textord/pithsync.cpp new file mode 100644 index 0000000000..4990044da8 --- /dev/null +++ b/textord/pithsync.cpp @@ -0,0 +1,696 @@ +/********************************************************************** + * File: pithsync.cpp (Formerly pitsync2.c) + * Description: Code to find the optimum fixed pitch segmentation of some blobs. + * Author: Ray Smith + * Created: Thu Nov 19 11:48:05 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include +#include "memry.h" +#include "makerow.h" +#include "pitsync1.h" +#include "topitch.h" +#include "pithsync.h" +#include "tprintf.h" + +#define PROJECTION_MARGIN 10 //arbitrary + +#define EXTERN + +/********************************************************************** + * FPCUTPT::setup + * + * Constructor to make a new FPCUTPT. + **********************************************************************/ + +void FPCUTPT::setup( //constructor + FPCUTPT *cutpts, //predecessors + INT16 array_origin, //start coord + STATS *projection, //vertical occupation + INT16 zero_count, //official zero + INT16 pitch, //proposed pitch + INT16 x, //position + INT16 offset //dist to gap + ) { + //half of pitch + INT16 half_pitch = pitch / 2 - 1; + UINT32 lead_flag; //new flag + INT32 ind; //current position + + if (half_pitch > 31) + half_pitch = 31; + else if (half_pitch < 0) + half_pitch = 0; + lead_flag = 1 << half_pitch; + + pred = NULL; + mean_sum = 0; + sq_sum = offset * offset; + cost = sq_sum; + faked = FALSE; + terminal = FALSE; + fake_count = 0; + xpos = x; + region_index = 0; + mid_cuts = 0; + if (x == array_origin) { + back_balance = 0; + fwd_balance = 0; + for (ind = 0; ind <= half_pitch; ind++) { + fwd_balance >>= 1; + if (projection->pile_count (ind) > zero_count) + fwd_balance |= lead_flag; + } + } + else { + back_balance = cutpts[x - 1 - array_origin].back_balance << 1; + back_balance &= lead_flag + lead_flag - 1; + if (projection->pile_count (x) > zero_count) + back_balance |= 1; + fwd_balance = cutpts[x - 1 - array_origin].fwd_balance >> 1; + if (projection->pile_count (x + half_pitch) > zero_count) + fwd_balance |= lead_flag; + } +} + + +/********************************************************************** + * FPCUTPT::assign + * + * Constructor to make a new FPCUTPT. + **********************************************************************/ + +void FPCUTPT::assign( //constructor + FPCUTPT *cutpts, //predecessors + INT16 array_origin, //start coord + INT16 x, //position + BOOL8 faking, //faking this one + BOOL8 mid_cut, //cheap cut. + INT16 offset, //dist to gap + STATS *projection, //vertical occupation + float projection_scale, //scaling + INT16 zero_count, //official zero + INT16 pitch, //proposed pitch + INT16 pitch_error //allowed tolerance + ) { + int index; //test index + int balance_index; //for balance factor + INT16 balance_count; //ding factor + INT16 r_index; //test cut number + FPCUTPT *segpt; //segment point + INT32 dist; //from prev segment + double sq_dist; //squared distance + double mean; //mean pitch + double total; //total dists + double factor; //cost function + //half of pitch + INT16 half_pitch = pitch / 2 - 1; + UINT32 lead_flag; //new flag + + if (half_pitch > 31) + half_pitch = 31; + else if (half_pitch < 0) + half_pitch = 0; + lead_flag = 1 << half_pitch; + + back_balance = cutpts[x - 1 - array_origin].back_balance << 1; + back_balance &= lead_flag + lead_flag - 1; + if (projection->pile_count (x) > zero_count) + back_balance |= 1; + fwd_balance = cutpts[x - 1 - array_origin].fwd_balance >> 1; + if (projection->pile_count (x + half_pitch) > zero_count) + fwd_balance |= lead_flag; + + xpos = x; + cost = MAX_FLOAT32; + pred = NULL; + faked = faking; + terminal = FALSE; + region_index = 0; + fake_count = MAX_INT16; + for (index = x - pitch - pitch_error; index <= x - pitch + pitch_error; + index++) { + if (index >= array_origin) { + segpt = &cutpts[index - array_origin]; + dist = x - segpt->xpos; + if (!segpt->terminal && segpt->fake_count < MAX_INT16) { + balance_count = 0; + if (textord_balance_factor > 0) { + if (textord_fast_pitch_test) { + lead_flag = back_balance ^ segpt->fwd_balance; + balance_count = 0; + while (lead_flag != 0) { + balance_count++; + lead_flag &= lead_flag - 1; + } + } + else { + for (balance_index = 0; + index + balance_index < x - balance_index; + balance_index++) + balance_count += + (projection->pile_count (index + balance_index) <= + zero_count) ^ (projection->pile_count (x - + balance_index) + <= zero_count); + } + balance_count = + (INT16) (balance_count * textord_balance_factor / + projection_scale); + } + r_index = segpt->region_index + 1; + total = segpt->mean_sum + dist; + balance_count += offset; + sq_dist = + dist * dist + segpt->sq_sum + balance_count * balance_count; + mean = total / r_index; + factor = mean - pitch; + factor *= factor; + factor += sq_dist / (r_index) - mean * mean; + if (factor < cost && segpt->fake_count + faked <= fake_count) { + cost = factor; //find least cost + pred = segpt; //save path + mean_sum = total; + sq_sum = sq_dist; + fake_count = segpt->fake_count + faked; + mid_cuts = segpt->mid_cuts + mid_cut; + region_index = r_index; + } + } + } + } +} + + +/********************************************************************** + * FPCUTPT::assign_cheap + * + * Constructor to make a new FPCUTPT on the cheap. + **********************************************************************/ + +void FPCUTPT::assign_cheap( //constructor + FPCUTPT *cutpts, //predecessors + INT16 array_origin, //start coord + INT16 x, //position + BOOL8 faking, //faking this one + BOOL8 mid_cut, //cheap cut. + INT16 offset, //dist to gap + STATS *projection, //vertical occupation + float projection_scale, //scaling + INT16 zero_count, //official zero + INT16 pitch, //proposed pitch + INT16 pitch_error //allowed tolerance + ) { + int index; //test index + INT16 balance_count; //ding factor + INT16 r_index; //test cut number + FPCUTPT *segpt; //segment point + INT32 dist; //from prev segment + double sq_dist; //squared distance + double mean; //mean pitch + double total; //total dists + double factor; //cost function + //half of pitch + INT16 half_pitch = pitch / 2 - 1; + UINT32 lead_flag; //new flag + + if (half_pitch > 31) + half_pitch = 31; + else if (half_pitch < 0) + half_pitch = 0; + lead_flag = 1 << half_pitch; + + back_balance = cutpts[x - 1 - array_origin].back_balance << 1; + back_balance &= lead_flag + lead_flag - 1; + if (projection->pile_count (x) > zero_count) + back_balance |= 1; + fwd_balance = cutpts[x - 1 - array_origin].fwd_balance >> 1; + if (projection->pile_count (x + half_pitch) > zero_count) + fwd_balance |= lead_flag; + + xpos = x; + cost = MAX_FLOAT32; + pred = NULL; + faked = faking; + terminal = FALSE; + region_index = 0; + fake_count = MAX_INT16; + index = x - pitch; + if (index >= array_origin) { + segpt = &cutpts[index - array_origin]; + dist = x - segpt->xpos; + if (!segpt->terminal && segpt->fake_count < MAX_INT16) { + balance_count = 0; + if (textord_balance_factor > 0) { + lead_flag = back_balance ^ segpt->fwd_balance; + balance_count = 0; + while (lead_flag != 0) { + balance_count++; + lead_flag &= lead_flag - 1; + } + balance_count = (INT16) (balance_count * textord_balance_factor + / projection_scale); + } + r_index = segpt->region_index + 1; + total = segpt->mean_sum + dist; + balance_count += offset; + sq_dist = + dist * dist + segpt->sq_sum + balance_count * balance_count; + mean = total / r_index; + factor = mean - pitch; + factor *= factor; + factor += sq_dist / (r_index) - mean * mean; + cost = factor; //find least cost + pred = segpt; //save path + mean_sum = total; + sq_sum = sq_dist; + fake_count = segpt->fake_count + faked; + mid_cuts = segpt->mid_cuts + mid_cut; + region_index = r_index; + } + } +} + + +/********************************************************************** + * check_pitch_sync + * + * Construct the lattice of possible segmentation points and choose the + * optimal path. Return the optimal path only. + * The return value is a measure of goodness of the sync. + **********************************************************************/ + +double check_pitch_sync2( //find segmentation + BLOBNBOX_IT *blob_it, //blobs to do + INT16 blob_count, //no of blobs + INT16 pitch, //pitch estimate + INT16 pitch_error, //tolerance + STATS *projection, //vertical + INT16 projection_left, //edges //scale factor + INT16 projection_right, + float projection_scale, + INT16 &occupation_count, //no of occupied cells + FPSEGPT_LIST *seg_list, //output list + INT16 start, //start of good range + INT16 end //end of good range + ) { + BOOL8 faking; //illegal cut pt + BOOL8 mid_cut; //cheap cut pt. + INT16 x; //current coord + INT16 blob_index; //blob number + INT16 left_edge; //of word + INT16 right_edge; //of word + INT16 array_origin; //x coord of array + INT16 offset; //dist to legal area + INT16 zero_count; //projection zero + INT16 best_left_x = 0; //for equals + INT16 best_right_x = 0; //right edge + BOX this_box; //bounding box + BOX next_box; //box of next blob + FPSEGPT *segpt; //segment point + FPCUTPT *cutpts; //array of points + double best_cost; //best path + double mean_sum; //computes result + FPCUTPT *best_end; //end of best path + INT16 best_fake; //best fake level + INT16 best_count; //no of cuts + BLOBNBOX_IT this_it; //copy iterator + FPSEGPT_IT seg_it = seg_list; //output iterator + + // tprintf("Computing sync on word of %d blobs with pitch %d\n", + // blob_count, pitch); + // if (blob_count==8 && pitch==27) + // projection->print(stdout,TRUE); + zero_count = 0; + if (pitch < 3) + pitch = 3; //nothing ludicrous + if ((pitch - 3) / 2 < pitch_error) + pitch_error = (pitch - 3) / 2; + this_it = *blob_it; + this_box = box_next (&this_it);//get box + // left_edge=this_box.left(); //left of word + // right_edge=this_box.right(); + // for (blob_index=1;blob_indexright_edge) + // right_edge=this_box.right(); + // } + for (left_edge = projection_left; projection->pile_count (left_edge) == 0 + && left_edge < projection_right; left_edge++); + for (right_edge = projection_right; projection->pile_count (right_edge) == 0 + && right_edge > left_edge; right_edge--); + ASSERT_HOST (right_edge >= left_edge); + if (pitsync_linear_version >= 4) + return check_pitch_sync3 (projection_left, projection_right, zero_count, + pitch, pitch_error, projection, + projection_scale, occupation_count, seg_list, + start, end); + array_origin = left_edge - pitch; + cutpts = (FPCUTPT *) alloc_mem ((right_edge - left_edge + pitch * 2 + 1) + * sizeof (FPCUTPT)); + for (x = array_origin; x < left_edge; x++) + //free cuts + cutpts[x - array_origin].setup (cutpts, array_origin, projection, zero_count, pitch, x, 0); + for (offset = 0; offset <= pitch_error; offset++, x++) + //not quite free + cutpts[x - array_origin].setup (cutpts, array_origin, projection, zero_count, pitch, x, offset); + + this_it = *blob_it; + best_cost = MAX_FLOAT32; + best_end = NULL; + this_box = box_next (&this_it);//first box + next_box = box_next (&this_it);//second box + blob_index = 1; + while (x < right_edge - pitch_error) { + if (x > this_box.right () + pitch_error && blob_index < blob_count) { + this_box = next_box; + next_box = box_next (&this_it); + blob_index++; + } + faking = FALSE; + mid_cut = FALSE; + if (x <= this_box.left ()) + offset = 0; + else if (x <= this_box.left () + pitch_error) + offset = x - this_box.left (); + else if (x >= this_box.right ()) + offset = 0; + else if (x >= next_box.left () && blob_index < blob_count) { + offset = x - next_box.left (); + if (this_box.right () - x < offset) + offset = this_box.right () - x; + } + else if (x >= this_box.right () - pitch_error) + offset = this_box.right () - x; + else if (x - this_box.left () > pitch * pitsync_joined_edge + && this_box.right () - x > pitch * pitsync_joined_edge) { + mid_cut = TRUE; + offset = 0; + } + else { + faking = TRUE; + offset = projection->pile_count (x); + } + cutpts[x - array_origin].assign (cutpts, array_origin, x, + faking, mid_cut, offset, projection, + projection_scale, zero_count, pitch, + pitch_error); + x++; + } + + best_fake = MAX_INT16; + best_cost = MAX_INT32; + best_count = MAX_INT16; + while (x < right_edge + pitch) { + offset = x < right_edge ? right_edge - x : 0; + cutpts[x - array_origin].assign (cutpts, array_origin, x, + FALSE, FALSE, offset, projection, + projection_scale, zero_count, pitch, + pitch_error); + cutpts[x - array_origin].terminal = TRUE; + if (cutpts[x - array_origin].index () + + cutpts[x - array_origin].fake_count <= best_count + best_fake) { + if (cutpts[x - array_origin].fake_count < best_fake + || cutpts[x - array_origin].fake_count == best_fake + && cutpts[x - array_origin].cost_function () < best_cost) { + best_fake = cutpts[x - array_origin].fake_count; + best_cost = cutpts[x - array_origin].cost_function (); + best_left_x = x; + best_right_x = x; + best_count = cutpts[x - array_origin].index (); + } + else if (cutpts[x - array_origin].fake_count == best_fake + && x == best_right_x + 1 + && cutpts[x - array_origin].cost_function () == best_cost) { + //exactly equal + best_right_x = x; + } + } + x++; + } + ASSERT_HOST (best_fake < MAX_INT16); + + best_end = &cutpts[(best_left_x + best_right_x) / 2 - array_origin]; + if (this_box.right () == textord_test_x + && this_box.top () == textord_test_y) { + for (x = left_edge - pitch; x < right_edge + pitch; x++) { + tprintf ("x=%d, C=%g, s=%g, sq=%g, prev=%d\n", + x, cutpts[x - array_origin].cost_function (), + cutpts[x - array_origin].sum (), + cutpts[x - array_origin].squares (), + cutpts[x - array_origin].previous ()->position ()); + } + } + occupation_count = -1; + do { + for (x = best_end->position () - pitch + pitch_error; + x < best_end->position () - pitch_error + && projection->pile_count (x) == 0; x++); + if (x < best_end->position () - pitch_error) + occupation_count++; + //copy it + segpt = new FPSEGPT (best_end); + seg_it.add_before_then_move (segpt); + best_end = best_end->previous (); + } + while (best_end != NULL); + seg_it.move_to_last (); + mean_sum = seg_it.data ()->sum (); + mean_sum = mean_sum * mean_sum / best_count; + if (seg_it.data ()->squares () - mean_sum < 0) + tprintf ("Impossible sqsum=%g, mean=%g, total=%d\n", + seg_it.data ()->squares (), seg_it.data ()->sum (), best_count); + free_mem(cutpts); + // tprintf("blob_count=%d, pitch=%d, sync=%g, occ=%d\n", + // blob_count,pitch,seg_it.data()->squares()-mean_sum, + // occupation_count); + return seg_it.data ()->squares () - mean_sum; +} + + +/********************************************************************** + * check_pitch_sync + * + * Construct the lattice of possible segmentation points and choose the + * optimal path. Return the optimal path only. + * The return value is a measure of goodness of the sync. + **********************************************************************/ + +double check_pitch_sync3( //find segmentation + INT16 projection_left, //edges //to be considered 0 + INT16 projection_right, + INT16 zero_count, + INT16 pitch, //pitch estimate + INT16 pitch_error, //tolerance + STATS *projection, //vertical + float projection_scale, //scale factor + INT16 &occupation_count, //no of occupied cells + FPSEGPT_LIST *seg_list, //output list + INT16 start, //start of good range + INT16 end //end of good range + ) { + BOOL8 faking; //illegal cut pt + BOOL8 mid_cut; //cheap cut pt. + INT16 left_edge; //of word + INT16 right_edge; //of word + INT16 x; //current coord + INT16 array_origin; //x coord of array + INT16 offset; //dist to legal area + INT16 projection_offset; //from scaled projection + INT16 prev_zero; //previous zero dist + INT16 next_zero; //next zero dist + INT16 zero_offset; //scan window + INT16 best_left_x = 0; //for equals + INT16 best_right_x = 0; //right edge + FPSEGPT *segpt; //segment point + FPCUTPT *cutpts; //array of points + BOOL8 *mins; //local min results + int minindex; //next input position + int test_index; //index to mins + double best_cost; //best path + double mean_sum; //computes result + FPCUTPT *best_end; //end of best path + INT16 best_fake; //best fake level + INT16 best_count; //no of cuts + FPSEGPT_IT seg_it = seg_list; //output iterator + + end = (end - start) % pitch; + if (pitch < 3) + pitch = 3; //nothing ludicrous + if ((pitch - 3) / 2 < pitch_error) + pitch_error = (pitch - 3) / 2; + //min dist of zero + zero_offset = (INT16) (pitch * pitsync_joined_edge); + for (left_edge = projection_left; projection->pile_count (left_edge) == 0 + && left_edge < projection_right; left_edge++); + for (right_edge = projection_right; projection->pile_count (right_edge) == 0 + && right_edge > left_edge; right_edge--); + array_origin = left_edge - pitch; + cutpts = (FPCUTPT *) alloc_mem ((right_edge - left_edge + pitch * 2 + 1) + * sizeof (FPCUTPT)); + mins = (BOOL8 *) alloc_mem ((pitch_error * 2 + 1) * sizeof (BOOL8)); + for (x = array_origin; x < left_edge; x++) + //free cuts + cutpts[x - array_origin].setup (cutpts, array_origin, projection, zero_count, pitch, x, 0); + prev_zero = left_edge - 1; + for (offset = 0; offset <= pitch_error; offset++, x++) + //not quite free + cutpts[x - array_origin].setup (cutpts, array_origin, projection, zero_count, pitch, x, offset); + + best_cost = MAX_FLOAT32; + best_end = NULL; + for (offset = -pitch_error, minindex = 0; offset < pitch_error; + offset++, minindex++) + mins[minindex] = projection->local_min (x + offset); + next_zero = x + zero_offset + 1; + for (offset = next_zero - 1; offset >= x; offset--) { + if (projection->pile_count (offset) <= zero_count) { + next_zero = offset; + break; + } + } + while (x < right_edge - pitch_error) { + mins[minindex] = projection->local_min (x + pitch_error); + minindex++; + if (minindex > pitch_error * 2) + minindex = 0; + faking = FALSE; + mid_cut = FALSE; + offset = 0; + if (projection->pile_count (x) <= zero_count) { + prev_zero = x; + } + else { + for (offset = 1; offset <= pitch_error; offset++) + if (projection->pile_count (x + offset) <= zero_count + || projection->pile_count (x - offset) <= zero_count) + break; + } + if (offset > pitch_error) { + if (x - prev_zero > zero_offset && next_zero - x > zero_offset) { + for (offset = 0; offset <= pitch_error; offset++) { + test_index = minindex + pitch_error + offset; + if (test_index > pitch_error * 2) + test_index -= pitch_error * 2 + 1; + if (mins[test_index]) + break; + test_index = minindex + pitch_error - offset; + if (test_index > pitch_error * 2) + test_index -= pitch_error * 2 + 1; + if (mins[test_index]) + break; + } + } + if (offset > pitch_error) { + offset = projection->pile_count (x); + faking = TRUE; + } + else { + projection_offset = + (INT16) (projection->pile_count (x) / projection_scale); + if (projection_offset > offset) + offset = projection_offset; + mid_cut = TRUE; + } + } + if (start == 0 && end == 0 + || !textord_fast_pitch_test + || (x - projection_left - start) % pitch <= end) + cutpts[x - array_origin].assign (cutpts, array_origin, x, + faking, mid_cut, offset, projection, + projection_scale, zero_count, pitch, + pitch_error); + else + cutpts[x - array_origin].assign_cheap (cutpts, array_origin, x, + faking, mid_cut, offset, + projection, projection_scale, + zero_count, pitch, + pitch_error); + x++; + if (next_zero < x || next_zero == x + zero_offset) + next_zero = x + zero_offset + 1; + if (projection->pile_count (x + zero_offset) <= zero_count) + next_zero = x + zero_offset; + } + + best_fake = MAX_INT16; + best_cost = MAX_INT32; + best_count = MAX_INT16; + while (x < right_edge + pitch) { + offset = x < right_edge ? right_edge - x : 0; + cutpts[x - array_origin].assign (cutpts, array_origin, x, + FALSE, FALSE, offset, projection, + projection_scale, zero_count, pitch, + pitch_error); + cutpts[x - array_origin].terminal = TRUE; + if (cutpts[x - array_origin].index () + + cutpts[x - array_origin].fake_count <= best_count + best_fake) { + if (cutpts[x - array_origin].fake_count < best_fake + || cutpts[x - array_origin].fake_count == best_fake + && cutpts[x - array_origin].cost_function () < best_cost) { + best_fake = cutpts[x - array_origin].fake_count; + best_cost = cutpts[x - array_origin].cost_function (); + best_left_x = x; + best_right_x = x; + best_count = cutpts[x - array_origin].index (); + } + else if (cutpts[x - array_origin].fake_count == best_fake + && x == best_right_x + 1 + && cutpts[x - array_origin].cost_function () == best_cost) { + //exactly equal + best_right_x = x; + } + } + x++; + } + ASSERT_HOST (best_fake < MAX_INT16); + + best_end = &cutpts[(best_left_x + best_right_x) / 2 - array_origin]; + // for (x=left_edge-pitch;xposition()); + // } + occupation_count = -1; + do { + for (x = best_end->position () - pitch + pitch_error; + x < best_end->position () - pitch_error + && projection->pile_count (x) == 0; x++); + if (x < best_end->position () - pitch_error) + occupation_count++; + //copy it + segpt = new FPSEGPT (best_end); + seg_it.add_before_then_move (segpt); + best_end = best_end->previous (); + } + while (best_end != NULL); + seg_it.move_to_last (); + mean_sum = seg_it.data ()->sum (); + mean_sum = mean_sum * mean_sum / best_count; + if (seg_it.data ()->squares () - mean_sum < 0) + tprintf ("Impossible sqsum=%g, mean=%g, total=%d\n", + seg_it.data ()->squares (), seg_it.data ()->sum (), best_count); + free_mem(mins); + free_mem(cutpts); + return seg_it.data ()->squares () - mean_sum; +} diff --git a/textord/pithsync.h b/textord/pithsync.h new file mode 100644 index 0000000000..f3e0839ca4 --- /dev/null +++ b/textord/pithsync.h @@ -0,0 +1,134 @@ +/********************************************************************** + * File: pithsync.h (Formerly pitsync2.h) + * Description: Code to find the optimum fixed pitch segmentation of some blobs. + * Author: Ray Smith + * Created: Thu Nov 19 11:48:05 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef PITHSYNC_H +#define PITHSYNC_H + +#include "blobbox.h" +#include "varable.h" +#include "statistc.h" +#include "notdll.h" + +class FPSEGPT_LIST; + +class FPCUTPT +{ + public: + FPCUTPT() { //empty + } + void setup ( //start of cut + FPCUTPT cutpts[], //predecessors + INT16 array_origin, //start coord + STATS * projection, //occupation + INT16 zero_count, //official zero + INT16 pitch, //proposed pitch + INT16 x, //position + INT16 offset); //dist to gap + + void assign ( //evaluate cut + FPCUTPT cutpts[], //predecessors + INT16 array_origin, //start coord + INT16 x, //position + BOOL8 faking, //faking this one + BOOL8 mid_cut, //doing free cut + INT16 offset, //extra cost dist + STATS * projection, //occupation + float projection_scale, //scaling + INT16 zero_count, //official zero + INT16 pitch, //proposed pitch + INT16 pitch_error); //allowed tolerance + + void assign_cheap ( //evaluate cut + FPCUTPT cutpts[], //predecessors + INT16 array_origin, //start coord + INT16 x, //position + BOOL8 faking, //faking this one + BOOL8 mid_cut, //doing free cut + INT16 offset, //extra cost dist + STATS * projection, //occupation + float projection_scale, //scaling + INT16 zero_count, //official zero + INT16 pitch, //proposed pitch + INT16 pitch_error); //allowed tolerance + + INT32 position() { //acces func + return xpos; + } + double cost_function() { + return cost; + } + double squares() { + return sq_sum; + } + double sum() { + return mean_sum; + } + FPCUTPT *previous() { + return pred; + } + INT16 cheap_cuts() const { //no of mi cuts + return mid_cuts; + } + INT16 index() const { + return region_index; + } + + BOOL8 faked; //faked split point + BOOL8 terminal; //successful end + INT16 fake_count; //total fakes to here + + private: + INT16 region_index; //cut serial number + INT16 mid_cuts; //no of cheap cuts + INT32 xpos; //location + UINT32 back_balance; //proj backwards + UINT32 fwd_balance; //proj forwards + FPCUTPT *pred; //optimal previous + double mean_sum; //mean so far + double sq_sum; //summed distsances + double cost; //cost function +}; +double check_pitch_sync2( //find segmentation + BLOBNBOX_IT *blob_it, //blobs to do + INT16 blob_count, //no of blobs + INT16 pitch, //pitch estimate + INT16 pitch_error, //tolerance + STATS *projection, //vertical + INT16 projection_left, //edges //scale factor + INT16 projection_right, + float projection_scale, + INT16 &occupation_count, //no of occupied cells + FPSEGPT_LIST *seg_list, //output list + INT16 start, //start of good range + INT16 end //end of good range + ); +double check_pitch_sync3( //find segmentation + INT16 projection_left, //edges //to be considered 0 + INT16 projection_right, + INT16 zero_count, + INT16 pitch, //pitch estimate + INT16 pitch_error, //tolerance + STATS *projection, //vertical + float projection_scale, //scale factor + INT16 &occupation_count, //no of occupied cells + FPSEGPT_LIST *seg_list, //output list + INT16 start, //start of good range + INT16 end //end of good range + ); +#endif diff --git a/textord/pitsync1.cpp b/textord/pitsync1.cpp new file mode 100644 index 0000000000..fe1fc26da5 --- /dev/null +++ b/textord/pitsync1.cpp @@ -0,0 +1,425 @@ +/********************************************************************** + * File: pitsync1.cpp (Formerly pitsync.c) + * Description: Code to find the optimum fixed pitch segmentation of some blobs. + * Author: Ray Smith + * Created: Thu Nov 19 11:48:05 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include +#include "memry.h" +#include "pitsync1.h" + +#include "notdll.h" + +ELISTIZE (FPSEGPT) CLISTIZE (FPSEGPT_LIST) +#define EXTERN +EXTERN +INT_VAR (pitsync_linear_version, 6, "Use new fast algorithm"); +EXTERN +double_VAR (pitsync_joined_edge, 0.75, +"Dist inside big blob for chopping"); +EXTERN +double_VAR (pitsync_offset_freecut_fraction, 0.25, +"Fraction of cut for free cuts"); +EXTERN +INT_VAR (pitsync_fake_depth, 1, "Max advance fake generation"); + +/********************************************************************** + * FPSEGPT::FPSEGPT + * + * Constructor to make a new FPSEGPT. + * The existing FPCUTPT is duplicated. + **********************************************************************/ + +FPSEGPT::FPSEGPT( //constructor + FPCUTPT *cutpt //create from new form + ) { + pred = NULL; + mean_sum = cutpt->sum (); + sq_sum = cutpt->squares (); + cost = cutpt->cost_function (); + faked = cutpt->faked; + terminal = cutpt->terminal; + fake_count = cutpt->fake_count; + xpos = cutpt->position (); + mid_cuts = cutpt->cheap_cuts (); +} + + +/********************************************************************** + * FPSEGPT::FPSEGPT + * + * Constructor to make a new FPSEGPT. + **********************************************************************/ + +FPSEGPT::FPSEGPT ( //constructor +INT16 x //position +):xpos (x) { + pred = NULL; + mean_sum = 0; + sq_sum = 0; + cost = 0; + faked = FALSE; + terminal = FALSE; + fake_count = 0; + mid_cuts = 0; +} + + +/********************************************************************** + * FPSEGPT::FPSEGPT + * + * Constructor to make a new FPSEGPT. + **********************************************************************/ + +FPSEGPT::FPSEGPT ( //constructor +INT16 x, //position +BOOL8 faking, //faking this one +INT16 offset, //dist to gap +INT16 region_index, //segment number +INT16 pitch, //proposed pitch +INT16 pitch_error, //allowed tolerance +FPSEGPT_LIST * prev_list //previous segment +):xpos (x) { + INT16 best_fake; //on previous + FPSEGPT *segpt; //segment point + INT32 dist; //from prev segment + double sq_dist; //squared distance + double mean; //mean pitch + double total; //total dists + double factor; //cost function + FPSEGPT_IT pred_it = prev_list;//for previuos segment + + cost = MAX_FLOAT32; + pred = NULL; + faked = faking; + terminal = FALSE; + best_fake = MAX_INT16; + mid_cuts = 0; + for (pred_it.mark_cycle_pt (); !pred_it.cycled_list (); pred_it.forward ()) { + segpt = pred_it.data (); + if (segpt->fake_count < best_fake) + best_fake = segpt->fake_count; + dist = x - segpt->xpos; + if (dist >= pitch - pitch_error && dist <= pitch + pitch_error + && !segpt->terminal) { + total = segpt->mean_sum + dist; + sq_dist = dist * dist + segpt->sq_sum + offset * offset; + //sum of squarees + mean = total / region_index; + factor = mean - pitch; + factor *= factor; + factor += sq_dist / (region_index) - mean * mean; + if (factor < cost) { + cost = factor; //find least cost + pred = segpt; //save path + mean_sum = total; + sq_sum = sq_dist; + fake_count = segpt->fake_count + faked; + } + } + } + if (fake_count > best_fake + 1) + pred = NULL; //fail it +} + + +/********************************************************************** + * check_pitch_sync + * + * Construct the lattice of possible segmentation points and choose the + * optimal path. Return the optimal path only. + * The return value is a measure of goodness of the sync. + **********************************************************************/ + +double check_pitch_sync( //find segmentation + BLOBNBOX_IT *blob_it, //blobs to do + INT16 blob_count, //no of blobs + INT16 pitch, //pitch estimate + INT16 pitch_error, //tolerance + STATS *projection, //vertical + FPSEGPT_LIST *seg_list //output list + ) { + INT16 x; //current coord + INT16 min_index; //blob number + INT16 max_index; //blob number + INT16 left_edge; //of word + INT16 right_edge; //of word + INT16 right_max; //max allowed x + INT16 min_x; //in this region + INT16 max_x; + INT16 region_index; + INT16 best_region_index = 0; //for best result + INT16 offset; //dist to legal area + INT16 left_best_x; //edge of good region + INT16 right_best_x; //right edge + BOX min_box; //bounding box + BOX max_box; //bounding box + BOX next_box; //box of next blob + FPSEGPT *segpt; //segment point + FPSEGPT_LIST *segpts; //points in a segment + double best_cost; //best path + double mean_sum; //computes result + FPSEGPT *best_end; //end of best path + BLOBNBOX_IT min_it; //copy iterator + BLOBNBOX_IT max_it; //copy iterator + FPSEGPT_IT segpt_it; //iterator + //output segments + FPSEGPT_IT outseg_it = seg_list; + FPSEGPT_LIST_CLIST lattice; //list of lists + //region iterator + FPSEGPT_LIST_C_IT lattice_it = &lattice; + + // tprintf("Computing sync on word of %d blobs with pitch %d\n", + // blob_count, pitch); + // if (blob_count==8 && pitch==27) + // projection->print(stdout,TRUE); + if (pitch < 3) + pitch = 3; //nothing ludicrous + if ((pitch - 3) / 2 < pitch_error) + pitch_error = (pitch - 3) / 2; + min_it = *blob_it; + min_box = box_next (&min_it); //get box + // if (blob_count==8 && pitch==27) + // tprintf("1st box at (%d,%d)->(%d,%d)\n", + // min_box.left(),min_box.bottom(), + // min_box.right(),min_box.top()); + //left of word + left_edge = min_box.left () + pitch_error; + for (min_index = 1; min_index < blob_count; min_index++) { + min_box = box_next (&min_it); + // if (blob_count==8 && pitch==27) + // tprintf("Box at (%d,%d)->(%d,%d)\n", + // min_box.left(),min_box.bottom(), + // min_box.right(),min_box.top()); + } + right_edge = min_box.right (); //end of word + max_x = left_edge; + //min permissible + min_x = max_x - pitch + pitch_error * 2 + 1; + right_max = right_edge + pitch - pitch_error - 1; + segpts = new FPSEGPT_LIST; //list of points + segpt_it.set_to_list (segpts); + for (x = min_x; x <= max_x; x++) { + segpt = new FPSEGPT (x); //make a new one + //put in list + segpt_it.add_after_then_move (segpt); + } + //first segment + lattice_it.add_before_then_move (segpts); + min_index = 0; + region_index = 1; + best_cost = MAX_FLOAT32; + best_end = NULL; + min_it = *blob_it; + min_box = box_next (&min_it); //first box + do { + left_best_x = -1; + right_best_x = -1; + segpts = new FPSEGPT_LIST; //list of points + segpt_it.set_to_list (segpts); + min_x += pitch - pitch_error;//next limits + max_x += pitch + pitch_error; + while (min_box.right () < min_x && min_index < blob_count) { + min_index++; + min_box = box_next (&min_it); + } + max_it = min_it; + max_index = min_index; + max_box = min_box; + next_box = box_next (&max_it); + for (x = min_x; x <= max_x && x <= right_max; x++) { + while (x < right_edge && max_index < blob_count + && x > max_box.right ()) { + max_index++; + max_box = next_box; + next_box = box_next (&max_it); + } + if (x <= max_box.left () + pitch_error + || x >= max_box.right () - pitch_error || x >= right_edge + || max_index < blob_count - 1 && x >= next_box.left () + || x - max_box.left () > pitch * pitsync_joined_edge + && max_box.right () - x > pitch * pitsync_joined_edge) { + // || projection->local_min(x)) + if (x - max_box.left () > 0 + && x - max_box.left () <= pitch_error) + //dist to real break + offset = x - max_box.left (); + else if (max_box.right () - x > 0 + && max_box.right () - x <= pitch_error + && (max_index >= blob_count - 1 + || x < next_box.left ())) + offset = max_box.right () - x; + else + offset = 0; + // offset=pitsync_offset_freecut_fraction*projection->pile_count(x); + segpt = new FPSEGPT (x, FALSE, offset, region_index, + pitch, pitch_error, lattice_it.data ()); + } + else { + offset = projection->pile_count (x); + segpt = new FPSEGPT (x, TRUE, offset, region_index, + pitch, pitch_error, lattice_it.data ()); + } + if (segpt->previous () != NULL) { + segpt_it.add_after_then_move (segpt); + if (x >= right_edge - pitch_error) { + segpt->terminal = TRUE;//no more wanted + if (segpt->cost_function () < best_cost) { + best_cost = segpt->cost_function (); + //find least + best_end = segpt; + best_region_index = region_index; + left_best_x = x; + right_best_x = x; + } + else if (segpt->cost_function () == best_cost + && right_best_x == x - 1) + right_best_x = x; + } + } + else { + delete segpt; //no good + } + } + if (segpts->empty ()) { + if (best_end != NULL) + break; //already found one + make_illegal_segment (lattice_it.data (), min_box, min_it, + region_index, pitch, pitch_error, segpts); + } + else { + if (right_best_x > left_best_x + 1) { + left_best_x = (left_best_x + right_best_x + 1) / 2; + for (segpt_it.mark_cycle_pt (); !segpt_it.cycled_list () + && segpt_it.data ()->position () != left_best_x; + segpt_it.forward ()); + if (segpt_it.data ()->position () == left_best_x) + //middle of region + best_end = segpt_it.data (); + } + } + //new segment + lattice_it.add_before_then_move (segpts); + region_index++; + } + while (min_x < right_edge); + ASSERT_HOST (best_end != NULL);//must always find some + + for (lattice_it.mark_cycle_pt (); !lattice_it.cycled_list (); + lattice_it.forward ()) { + segpts = lattice_it.data (); + segpt_it.set_to_list (segpts); + // if (blob_count==8 && pitch==27) + // { + // for (segpt_it.mark_cycle_pt();!segpt_it.cycled_list();segpt_it.forward()) + // { + // segpt=segpt_it.data(); + // tprintf("At %d, (%x) cost=%g, m=%g, sq=%g, pred=%x\n", + // segpt->position(),segpt,segpt->cost_function(), + // segpt->sum(),segpt->squares(),segpt->previous()); + // } + // tprintf("\n"); + // } + for (segpt_it.mark_cycle_pt (); !segpt_it.cycled_list () + && segpt_it.data () != best_end; segpt_it.forward ()); + if (segpt_it.data () == best_end) { + //save good one + segpt = segpt_it.extract (); + outseg_it.add_before_then_move (segpt); + best_end = segpt->previous (); + } + } + ASSERT_HOST (best_end == NULL); + ASSERT_HOST (!outseg_it.empty ()); + outseg_it.move_to_last (); + mean_sum = outseg_it.data ()->sum (); + mean_sum = mean_sum * mean_sum / best_region_index; + if (outseg_it.data ()->squares () - mean_sum < 0) + tprintf ("Impossible sqsum=%g, mean=%g, total=%d\n", + outseg_it.data ()->squares (), outseg_it.data ()->sum (), + best_region_index); + lattice.deep_clear (); //shift the lot + return outseg_it.data ()->squares () - mean_sum; +} + + +/********************************************************************** + * make_illegal_segment + * + * Make a fake set of chop points due to having no legal places. + **********************************************************************/ + +void make_illegal_segment( //find segmentation + FPSEGPT_LIST *prev_list, //previous segments + BOX blob_box, //bounding box + BLOBNBOX_IT blob_it, //iterator + INT16 region_index, //number of segment + INT16 pitch, //pitch estimate + INT16 pitch_error, //tolerance + FPSEGPT_LIST *seg_list //output list + ) { + INT16 x; //current coord + INT16 min_x = 0; //in this region + INT16 max_x = 0; + INT16 offset; //dist to edge + FPSEGPT *segpt; //segment point + FPSEGPT *prevpt; //previous point + float best_cost; //best path + FPSEGPT_IT segpt_it = seg_list;//iterator + //previous points + FPSEGPT_IT prevpt_it = prev_list; + + best_cost = MAX_FLOAT32; + for (prevpt_it.mark_cycle_pt (); !prevpt_it.cycled_list (); + prevpt_it.forward ()) { + prevpt = prevpt_it.data (); + if (prevpt->cost_function () < best_cost) { + //find least + best_cost = prevpt->cost_function (); + min_x = prevpt->position (); + max_x = min_x; //limits on coords + } + else if (prevpt->cost_function () == best_cost) { + max_x = prevpt->position (); + } + } + min_x += pitch - pitch_error; + max_x += pitch + pitch_error; + for (x = min_x; x <= max_x; x++) { + while (x > blob_box.right ()) { + blob_box = box_next (&blob_it); + } + offset = x - blob_box.left (); + if (blob_box.right () - x < offset) + offset = blob_box.right () - x; + segpt = new FPSEGPT (x, FALSE, offset, + region_index, pitch, pitch_error, prev_list); + if (segpt->previous () != NULL) { + ASSERT_HOST (offset >= 0); + fprintf (stderr, "made fake at %d\n", x); + //make one up + segpt_it.add_after_then_move (segpt); + segpt->faked = TRUE; + segpt->fake_count++; + } + else + delete segpt; + } +} diff --git a/textord/pitsync1.h b/textord/pitsync1.h new file mode 100644 index 0000000000..a2c8e779b2 --- /dev/null +++ b/textord/pitsync1.h @@ -0,0 +1,135 @@ +/********************************************************************** + * File: pitsync1.h (Formerly pitsync.h) + * Description: Code to find the optimum fixed pitch segmentation of some blobs. + * Author: Ray Smith + * Created: Thu Nov 19 11:48:05 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef PITSYNC1_H +#define PITSYNC1_H + +#include "elst.h" +#include "clst.h" +#include "blobbox.h" +#include "varable.h" +#include "statistc.h" +#include "pithsync.h" +#include "notdll.h" +#include "notdll.h" + +class FPSEGPT_LIST; + +class FPSEGPT:public ELIST_LINK +{ + public: + FPSEGPT() { //empty + } + FPSEGPT( //constructor + INT16 x); //position + FPSEGPT( //constructor + INT16 x, //position + BOOL8 faking, //faking this one + INT16 offset, //extra cost dist + INT16 region_index, //segment number + INT16 pitch, //proposed pitch + INT16 pitch_error, //allowed tolerance + FPSEGPT_LIST *prev_list); //previous segment + FPSEGPT(FPCUTPT *cutpt); //build from new type + + INT32 position() { //acces func + return xpos; + } + double cost_function() { + return cost; + } + double squares() { + return sq_sum; + } + double sum() { + return mean_sum; + } + FPSEGPT *previous() { + return pred; + } + INT16 cheap_cuts() const { //no of cheap cuts + return mid_cuts; + } + + //faked split point + NEWDELETE2 (FPSEGPT) BOOL8 faked; + BOOL8 terminal; //successful end + INT16 fake_count; //total fakes to here + + private: + INT16 mid_cuts; //no of cheap cuts + INT32 xpos; //location + FPSEGPT *pred; //optimal previous + double mean_sum; //mean so far + double sq_sum; //summed distsances + double cost; //cost function +}; + +ELISTIZEH (FPSEGPT) CLISTIZEH (FPSEGPT_LIST) +extern +BOOL_VAR_H (pitsync_projection_fix, FALSE, +"Fix bug in projection profile"); +extern +INT_VAR_H (pitsync_linear_version, 0, "Use new fast algorithm"); +extern +double_VAR_H (pitsync_joined_edge, 0.75, +"Dist inside big blob for chopping"); +extern +double_VAR_H (pitsync_offset_freecut_fraction, 0.25, +"Fraction of cut for free cuts"); +extern +INT_VAR_H (pitsync_fake_depth, 1, "Max advance fake generation"); +double check_pitch_sync( //find segmentation + BLOBNBOX_IT *blob_it, //blobs to do + INT16 blob_count, //no of blobs + INT16 pitch, //pitch estimate + INT16 pitch_error, //tolerance + STATS *projection, //vertical + FPSEGPT_LIST *seg_list //output list + ); +void make_illegal_segment( //find segmentation + FPSEGPT_LIST *prev_list, //previous segments + BOX blob_box, //bounding box + BLOBNBOX_IT blob_it, //iterator + INT16 region_index, //number of segment + INT16 pitch, //pitch estimate + INT16 pitch_error, //tolerance + FPSEGPT_LIST *seg_list //output list + ); +INT16 vertical_torow_projection( //project whole row + TO_ROW *row, //row to do + STATS *projection //output + ); +void vertical_blob_projection( //project outlines + PBLOB *blob, //blob to project + STATS *stats //output + ); +void vertical_outline_projection( //project outlines + OUTLINE *outline, //outline to project + STATS *stats //output + ); +void vertical_cblob_projection( //project outlines + C_BLOB *blob, //blob to project + STATS *stats //output + ); +void vertical_coutline_projection( //project outlines + C_OUTLINE *outline, //outline to project + STATS *stats //output + ); +#endif diff --git a/textord/scanedg.cpp b/textord/scanedg.cpp new file mode 100644 index 0000000000..b66badc5ff --- /dev/null +++ b/textord/scanedg.cpp @@ -0,0 +1,461 @@ +/********************************************************************** + * File: scanedg.c (Formerly scanedge.c) + * Description: Raster scanning crack based edge extractor. + * Author: Ray Smith + * Created: Fri Mar 22 16:11:50 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "edgloop.h" +//#include "dirtab.h" +#include "scanedg.h" + +#define WHITE_PIX 1 /*thresholded colours */ +#define BLACK_PIX 0 + /*W->B->W */ +#define FLIP_COLOUR(pix) (1-(pix)) + +#define EWSIZE 4 /*edge operator size */ + +#define XMARGIN 2 //margin needed +#define YMARGIN 3 //by edge detector + + /*local freelist */ +static CRACKEDGE *free_cracks = NULL; + +/********************************************************************** + * block_edges + * + * Extract edges from a PDBLK. + **********************************************************************/ + +DLLSYM void block_edges( //get edges in a block + IMAGE *t_image, //threshold image + PDBLK *block, //block in image + ICOORD page_tr //corner of page + ) { + UINT8 margin; //margin colour + INT16 x; //line coords + INT16 y; //current line + ICOORD bleft; //bounding box + ICOORD tright; + ICOORD block_bleft; //bounding box + ICOORD block_tright; + int xindex; //index to pixel + BLOCK_LINE_IT line_it = block; //line iterator + IMAGELINE bwline; //thresholded line + //lines in progress + CRACKEDGE *ptrlinemem[MAXIMAGEWIDTH]; + CRACKEDGE **ptrline = ptrlinemem; + + if (t_image->get_xsize()+1 > MAXIMAGEWIDTH) { + ptrline = new CRACKEDGE*[t_image->get_xsize()+1]; + } + + //block box + block->bounding_box (bleft, tright); + block_bleft = bleft; + block_tright = tright; + for (x = tright.x () - bleft.x (); x >= 0; x--) + ptrline[x] = NULL; //no lines in progress + + bwline.init (t_image->get_xsize()); + + margin = WHITE; + + for (y = tright.y () - 1; y >= bleft.y () - 1; y--) { + if (y >= block_bleft.y () && y < block_tright.y ()) { + t_image->get_line (bleft.x (), y, tright.x () - bleft.x (), &bwline, + 0); + make_margins (block, &line_it, bwline.pixels, margin, bleft.x (), + tright.x (), y); + } + else { + x = tright.x () - bleft.x (); + for (xindex = 0; xindex < x; xindex++) + bwline.pixels[xindex] = margin; + } + line_edges (bleft.x (), y, tright.x () - bleft.x (), + margin, bwline.pixels, ptrline); + } + + free_crackedges(free_cracks); //really free them + free_cracks = NULL; + if (ptrline != ptrlinemem) { + delete [] ptrline; + } +} + + +/********************************************************************** + * make_margins + * + * Get an image line and set to margin non-text pixels. + **********************************************************************/ + +void make_margins( //get a line + PDBLK *block, //block in image + BLOCK_LINE_IT *line_it, //for old style + UINT8 *pixels, //pixels to strip + UINT8 margin, //white-out pixel + INT16 left, //block edges + INT16 right, + INT16 y //line coord + ) { + PB_LINE_IT *lines; + ICOORDELT_LIST *segments; //bits of a line + ICOORDELT_IT seg_it; + INT32 start; //of segment + INT16 xext; //of segment + int xindex; //index to pixel + + if (block->poly_block () != NULL) { + lines = new PB_LINE_IT (block->poly_block ()); + segments = lines->get_line (y); + if (!segments->empty ()) { + seg_it.set_to_list (segments); + seg_it.mark_cycle_pt (); + start = seg_it.data ()->x (); + xext = seg_it.data ()->y (); + for (xindex = left; xindex < right; xindex++) { + if (xindex >= start && !seg_it.cycled_list ()) { + xindex = start + xext - 1; + seg_it.forward (); + start = seg_it.data ()->x (); + xext = seg_it.data ()->y (); + } + else + pixels[xindex - left] = margin; + } + } + else { + for (xindex = left; xindex < right; xindex++) + pixels[xindex - left] = margin; + } + delete segments; + delete lines; + } + else { + start = line_it->get_line (y, xext); + for (xindex = left; xindex < start; xindex++) + pixels[xindex - left] = margin; + for (xindex = start + xext; xindex < right; xindex++) + pixels[xindex - left] = margin; + } +} + + +/********************************************************************** + * whiteout_block + * + * Extract edges from a PDBLK. + **********************************************************************/ + +void whiteout_block( //clean it + IMAGE *t_image, //threshold image + PDBLK *block //block in image + ) { + INT16 x; //line coords + INT16 y; //current line + INT16 xext; //line width + int xindex; //index to pixel + UINT8 *dest; //destination pixel + BOX block_box; //bounding box + BLOCK_LINE_IT line_it = block; //line iterator + IMAGELINE bwline; //thresholded line + + block_box = block->bounding_box (); + for (y = block_box.bottom (); y < block_box.top (); y++) { + //find line limits + x = line_it.get_line (y, xext); + t_image->get_line (x, y, xext, &bwline, 0); + dest = bwline.pixels; //destination pixel + for (xindex = 0; xindex < xext; xindex++) + *dest++ = 1; + t_image->put_line (x, y, xext, &bwline, 0); + } +} + + +/********************************************************************** + * line_edges + * + * Scan a line for edges and update the edges in progress. + * When edges close into loops, send them for approximation. + **********************************************************************/ + +void +line_edges ( //scan for edges +INT16 x, //coord of line start +INT16 y, //coord of line +INT16 xext, //width of line +UINT8 uppercolour, //start of prev line +UINT8 * bwpos, //thresholded line +CRACKEDGE ** prevline //edges in progress +) { + int xpos; //current x coord + int xmax; //max x coord + int colour; //of current pixel + int prevcolour; //of previous pixel + CRACKEDGE *current; //current h edge + CRACKEDGE *newcurrent; //new h edge + + xmax = x + xext; //max allowable coord + prevcolour = uppercolour; //forced plain margin + current = NULL; //nothing yet + + //do each pixel + for (xpos = x; xpos < xmax; xpos++, prevline++) { + colour = *bwpos++; //current pixel + if (*prevline != NULL) { + //changed above + //change colour + uppercolour = FLIP_COLOUR (uppercolour); + if (colour == prevcolour) { + if (colour == uppercolour) { + //finish a line + join_edges(current, *prevline); + current = NULL; //no edge now + } + else + //new horiz edge + current = h_edge (xpos, y, uppercolour - colour, *prevline); + *prevline = NULL; //no change this time + } + else { + if (colour == uppercolour) + *prevline = v_edge (xpos, y, colour - prevcolour, *prevline); + //8 vs 4 connection + else if (colour == WHITE_PIX) { + join_edges(current, *prevline); + current = h_edge (xpos, y, uppercolour - colour, NULL); + *prevline = v_edge (xpos, y, colour - prevcolour, current); + } + else { + newcurrent = h_edge (xpos, y, uppercolour - colour, *prevline); + *prevline = v_edge (xpos, y, colour - prevcolour, current); + current = newcurrent; //right going h edge + } + prevcolour = colour; //remember new colour + } + } + else { + if (colour != prevcolour) { + *prevline = current = + v_edge (xpos, y, colour - prevcolour, current); + prevcolour = colour; + } + if (colour != uppercolour) + current = h_edge (xpos, y, uppercolour - colour, current); + else + current = NULL; //no edge now + } + } + if (current != NULL) { + //out of block + if (*prevline != NULL) { //got one to join to? + join_edges(current, *prevline); + *prevline = NULL; //tidy now + } + else { + //fake vertical + *prevline = v_edge (xpos, y, FLIP_COLOUR(prevcolour)-prevcolour, current); + } + } + else if (*prevline != NULL) + //continue fake + *prevline = v_edge (xpos, y, FLIP_COLOUR(prevcolour)-prevcolour, *prevline); +} + + +/********************************************************************** + * h_edge + * + * Create a new horizontal CRACKEDGE and join it to the given edge. + **********************************************************************/ + +CRACKEDGE * +h_edge ( //horizontal edge +INT16 x, //xposition +INT16 y, //y position +INT8 sign, //sign of edge +CRACKEDGE * join //edge to join to +) { + CRACKEDGE *newpt; //return value + + // check_mem("h_edge",JUSTCHECKS); + if (free_cracks != NULL) { + newpt = free_cracks; + free_cracks = newpt->next; //get one fast + } + else { + newpt = new CRACKEDGE; + } + newpt->pos.set_y (y + 1); //coords of pt + newpt->stepy = 0; //edge is horizontal + + if (sign > 0) { + newpt->pos.set_x (x + 1); //start location + newpt->stepx = -1; + newpt->stepdir = 0; + } + else { + newpt->pos.set_x (x); //start location + newpt->stepx = 1; + newpt->stepdir = 2; + } + + if (join == NULL) { + newpt->next = newpt; //ptrs to other ends + newpt->prev = newpt; + } + else { + if (newpt->pos.x () + newpt->stepx == join->pos.x () + && newpt->pos.y () == join->pos.y ()) { + newpt->prev = join->prev; //update other ends + newpt->prev->next = newpt; + newpt->next = join; //join up + join->prev = newpt; + } + else { + newpt->next = join->next; //update other ends + newpt->next->prev = newpt; + newpt->prev = join; //join up + join->next = newpt; + } + } + return newpt; +} + + +/********************************************************************** + * v_edge + * + * Create a new vertical CRACKEDGE and join it to the given edge. + **********************************************************************/ + +CRACKEDGE * +v_edge ( //vertical edge +INT16 x, //xposition +INT16 y, //y position +INT8 sign, //sign of edge +CRACKEDGE * join //edge to join to +) { + CRACKEDGE *newpt; //return value + + if (free_cracks != NULL) { + newpt = free_cracks; + free_cracks = newpt->next; //get one fast + } + else { + newpt = new CRACKEDGE; + } + newpt->pos.set_x (x); //coords of pt + newpt->stepx = 0; //edge is vertical + + if (sign > 0) { + newpt->pos.set_y (y); //start location + newpt->stepy = 1; + newpt->stepdir = 3; + } + else { + newpt->pos.set_y (y + 1); //start location + newpt->stepy = -1; + newpt->stepdir = 1; + } + + if (join == NULL) { + newpt->next = newpt; //ptrs to other ends + newpt->prev = newpt; + } + else { + if (newpt->pos.x () == join->pos.x () + && newpt->pos.y () + newpt->stepy == join->pos.y ()) { + newpt->prev = join->prev; //update other ends + newpt->prev->next = newpt; + newpt->next = join; //join up + join->prev = newpt; + } + else { + newpt->next = join->next; //update other ends + newpt->next->prev = newpt; + newpt->prev = join; //join up + join->next = newpt; + } + } + return newpt; +} + + +/********************************************************************** + * join_edges + * + * Join 2 edges together. Send the outline for approximation when a + * closed loop is formed. + **********************************************************************/ + +void join_edges( //join edge fragments + CRACKEDGE *edge1, //edges to join + CRACKEDGE *edge2 //no specific order + ) { + CRACKEDGE *tempedge; //for exchanging + + if (edge1->pos.x () + edge1->stepx != edge2->pos.x () + || edge1->pos.y () + edge1->stepy != edge2->pos.y ()) { + tempedge = edge1; + edge1 = edge2; //swap araound + edge2 = tempedge; + } + + // tprintf("Joining %x=(%d,%d)+(%d,%d)->%x<-%x ", + // edge1,edge1->pos.x(),edge1->pos.y(),edge1->stepx,edge1->stepy, + // edge1->next,edge1->prev); + // tprintf("to %x=(%d,%d)+(%d,%d)->%x<-%x\n", + // edge2,edge2->pos.x(),edge2->pos.y(),edge2->stepx,edge2->stepy, + // edge2->next,edge2->prev); + if (edge1->next == edge2) { + //already closed + complete_edge(edge1); //approximate it + //attach freelist to end + edge1->prev->next = free_cracks; + free_cracks = edge1; //and free list + } + else { + //update opposite ends + edge2->prev->next = edge1->next; + edge1->next->prev = edge2->prev; + edge1->next = edge2; //make joins + edge2->prev = edge1; + } +} + + +/********************************************************************** + * free_crackedges + * + * Really free the CRACKEDGEs by giving them back to delete. + **********************************************************************/ + +void free_crackedges( //really free them + CRACKEDGE *start //start of loop + ) { + CRACKEDGE *current; //current edge to free + CRACKEDGE *next; //next one to free + + for (current = start; current != NULL; current = next) { + next = current->next; + delete current; //delete them all + } +} diff --git a/textord/scanedg.h b/textord/scanedg.h new file mode 100644 index 0000000000..72ee331dc0 --- /dev/null +++ b/textord/scanedg.h @@ -0,0 +1,74 @@ +/********************************************************************** + * File: scanedg.h (Formerly scanedge.h) + * Description: Raster scanning crack based edge extractor. + * Author: Ray Smith + * Created: Fri Mar 22 16:11:50 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef SCANEDG_H +#define SCANEDG_H + +#include "varable.h" +#include "grphics.h" +#include "img.h" +#include "pdblock.h" +#include "crakedge.h" + +DLLSYM void block_edges( //get edges in a block + IMAGE *t_image, //threshold image + PDBLK *block, //block in image + ICOORD page_tr //corner of page + ); +void make_margins( //get a line + PDBLK *block, //block in image + BLOCK_LINE_IT *line_it, //for old style + UINT8 *pixels, //pixels to strip + UINT8 margin, //white-out pixel + INT16 left, //block edges + INT16 right, + INT16 y //line coord + ); +void whiteout_block( //clean it + IMAGE *t_image, //threshold image + PDBLK *block //block in image + ); +void line_edges ( //scan for edges +INT16 x, //coord of line start +INT16 y, //coord of line +INT16 xext, //width of line +UINT8 uppercolour, //start of prev line +UINT8 * bwpos, //thresholded line +CRACKEDGE ** prevline //edges in progress +); +CRACKEDGE *h_edge ( //horizontal edge +INT16 x, //xposition +INT16 y, //y position +INT8 sign, //sign of edge +CRACKEDGE * join //edge to join to +); +CRACKEDGE *v_edge ( //vertical edge +INT16 x, //xposition +INT16 y, //y position +INT8 sign, //sign of edge +CRACKEDGE * join //edge to join to +); +void join_edges( //join edge fragments + CRACKEDGE *edge1, //edges to join + CRACKEDGE *edge2 //no specific order + ); +void free_crackedges( //really free them + CRACKEDGE *start //start of loop + ); +#endif diff --git a/textord/sortflts.cpp b/textord/sortflts.cpp new file mode 100644 index 0000000000..bf799601cb --- /dev/null +++ b/textord/sortflts.cpp @@ -0,0 +1,80 @@ +/********************************************************************** + * File: sortflts.cpp (Formerly sfloats.c) + * Description: Code to maintain a sorted list of floats. + * Author: Ray Smith + * Created: Mon Oct 4 16:15:40 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "sortflts.h" +#include "notdll.h" + +ELISTIZE (SORTED_FLOAT) +/********************************************************************** + * SORTED_FLOATS::add + * + * Add a new entry to the sorted lsit of floats. + **********************************************************************/ +void SORTED_FLOATS::add( //add new entry + float value, + INT32 key) { + SORTED_FLOAT *new_float = new SORTED_FLOAT (value, key); + + if (list.empty ()) + it.add_after_stay_put (new_float); + else { + it.move_to_first (); + while (!it.at_last () && it.data ()->entry < value) + it.forward (); + if (it.data ()->entry < value) + it.add_after_stay_put (new_float); + else + it.add_before_stay_put (new_float); + } +} + + +/********************************************************************** + * SORTED_FLOATS::remove + * + * Remove an entry from the sorted lsit of floats. + **********************************************************************/ + +void SORTED_FLOATS::remove( //remove the entry + INT32 key) { + if (!list.empty ()) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + if (it.data ()->address == key) { + delete it.extract (); + return; + } + } + } +} + + +/********************************************************************** + * SORTED_FLOATS::operator[] + * + * Return the floating point value of the given index into the list. + **********************************************************************/ + +float +SORTED_FLOATS::operator[] ( //get an entry +INT32 index //to list +) { + it.move_to_first (); + return it.data_relative (index)->entry; +} diff --git a/textord/sortflts.h b/textord/sortflts.h new file mode 100644 index 0000000000..7ad97d375a --- /dev/null +++ b/textord/sortflts.h @@ -0,0 +1,64 @@ +/********************************************************************** + * File: sortflts.h (Formerly sfloats.h) + * Description: Code to maintain a sorted list of floats. + * Author: Ray Smith + * Created: Mon Oct 4 16:15:40 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef SORTFLTS_H +#define SORTFLTS_H + +#include "elst.h" +#include "notdll.h" +#include "notdll.h" + +class SORTED_FLOAT:public ELIST_LINK +{ + friend class SORTED_FLOATS; + + public: + SORTED_FLOAT() { + } //empty constructor + SORTED_FLOAT( //create one + float value, //value of entry + INT32 key) { //reference + entry = value; + address = key; + } + private: + float entry; //value of float + INT32 address; //key +}; + +ELISTIZEH (SORTED_FLOAT) +class SORTED_FLOATS +{ + public: + SORTED_FLOATS() { //empty constructor + it.set_to_list (&list); + } + void add( //add sample + float value, //sample float + INT32 key); //retrieval key + void remove( //delete sample + INT32 key); //key to delete + float operator[] ( //index to list + INT32 index); //item to get + + private: + SORTED_FLOAT_LIST list; //list of floats + SORTED_FLOAT_IT it; //iterator built-in +}; +#endif diff --git a/textord/tessout.h b/textord/tessout.h new file mode 100644 index 0000000000..bd850dde5d --- /dev/null +++ b/textord/tessout.h @@ -0,0 +1,76 @@ +/********************************************************************** + * File: tessout.h (Formerly tessconv.h) + * Description: Code to convert from tesseract data to mithras data. + * Author: Ray Smith + * Created: Tue Oct 22 12:54:38 BST 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TESSOUT_H +#define TESSOUT_H + +#include "ocrblock.h" +#include "tessclas.h" +#include "statistc.h" +#include "notdll.h" + +BOOL8 make_blocks_from_rows( //make thresholds + TEXTROW *tessrows, //old style rows + const char *name, //name of image + ICOORD page_tr, //page size + BOOL8 copy_poly, //true to copy poly + BLOCK_IT *block_it //blocks to make + ); +ROW *convert_row( //convert a row + TEXTROW *tessrow, //row to convert + BOOL8 do_shift, //true do do shift + INT16 &top, //top of row + INT16 &bottom //bottom of row + ); +void convert_words( //convert a row + TEXTROW *tessrow, //row to convert + BOOL8 do_shift, //true do do shift + ROW *row //destination + ); +PBLOB *convert_blob( //convert a blob + TBLOB *tblob, //blob to convert + BOOL8 do_shift //true do do shift + ); +void convert_outline( //convert a outline + TESSLINE *tessline, //outline to convert + BOOL8 do_shift, //true do do shift + BOOL8 reverse, //reverse it + OUTLINE_IT *it //output list + ); +void accumulate_word_stats( //get stats + TWERD *word, //word to do + STATS *kern_stats, //kerning + ICOORD &bleft, //corners + ICOORD &tright); +void blob_bounding_box( //get bounding box + TBLOB *blob, //blob to do + INT16 &xmin, //bounding box + INT16 &ymin, + INT16 &xmax, //of blob + INT16 &ymax); +void free_blob( //free tess blob + TBLOB *blob //blob to free + ); +void free_tree( //free outlines + TESSLINE *outline //outlines to free + ); +void free_outline( //free one + TESSLINE *outline //outline to free + ); +#endif diff --git a/textord/topitch.cpp b/textord/topitch.cpp new file mode 100644 index 0000000000..434f6a1db0 --- /dev/null +++ b/textord/topitch.cpp @@ -0,0 +1,2015 @@ +/********************************************************************** + * File: topitch.cpp (Formerly to_pitch.c) + * Description: Code to determine fixed pitchness and the pitch if fixed. + * Author: Ray Smith + * Created: Tue Aug 24 16:57:29 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include "stderr.h" +#include "blobbox.h" +#include "lmedsq.h" +#include "statistc.h" +#include "drawtord.h" +#include "makerow.h" +#include "pitsync1.h" +#include "pithsync.h" +#include "blobcmpl.h" +#include "tovars.h" +#include "wordseg.h" +#include "topitch.h" +#include "secname.h" + +#define EXTERN + +EXTERN BOOL_VAR (textord_all_prop, FALSE, "All doc is proportial text"); +EXTERN BOOL_VAR (textord_debug_pitch_test, FALSE, +"Debug on fixed pitch test"); +EXTERN BOOL_VAR (textord_disable_pitch_test, FALSE, +"Turn off dp fixed pitch algorithm"); +EXTERN BOOL_VAR (textord_fast_pitch_test, FALSE, +"Do even faster pitch algorithm"); +EXTERN BOOL_VAR (textord_debug_pitch_metric, FALSE, +"Write full metric stuff"); +EXTERN BOOL_VAR (textord_show_row_cuts, FALSE, "Draw row-level cuts"); +EXTERN BOOL_VAR (textord_show_page_cuts, FALSE, "Draw page-level cuts"); +EXTERN BOOL_VAR (textord_pitch_cheat, FALSE, +"Use correct answer for fixed/prop"); +EXTERN BOOL_VAR (textord_blockndoc_fixed, FALSE, +"Attempt whole doc/block fixed pitch"); +EXTERN double_VAR (textord_projection_scale, 0.200, "Ding rate for mid-cuts"); +EXTERN double_VAR (textord_balance_factor, 1.0, +"Ding rate for unbalanced char cells"); +EXTERN double_VAR (textord_repch_width_variance, 0.2, +"Max width change of gap/blob"); + +#define FIXED_WIDTH_MULTIPLE 5 +#define BLOCK_STATS_CLUSTERS 10 +#define MAX_ALLOWED_PITCH 100 //max pixel pitch. + +/********************************************************************** + * compute_fixed_pitch + * + * Decide whether each row is fixed pitch individually. + * Correlate definite and uncertain results to obtain an individual + * result for each row in the TO_ROW class. + **********************************************************************/ + +void compute_fixed_pitch( //determine pitch + ICOORD page_tr, //top right + TO_BLOCK_LIST *port_blocks, //input list + float gradient, //page skew + FCOORD rotation, //for drawing + BOOL8 testing_on //correct orientation + ) { + TO_BLOCK_IT block_it; //iterator + TO_BLOCK *block; //current block; + TO_ROW_IT row_it; //row iterator + TO_ROW *row; //current row + int block_index; //block number + int row_index; //row number + +#ifndef GRAPHICS_DISABLED + if (textord_show_initial_words && testing_on) { + if (to_win == NO_WINDOW) + create_to_win(page_tr); + } +#endif + + block_it.set_to_list (port_blocks); + block_index = 1; + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + compute_block_pitch(block, rotation, block_index, testing_on); + block_index++; + } + + if (!try_doc_fixed (page_tr, port_blocks, gradient)) { + block_index = 1; + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + if (!try_block_fixed (block, block_index)) + try_rows_fixed(block, block_index, testing_on); + block_index++; + } + } + + block_index = 1; + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->get_rows ()); + row_index = 1; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + fix_row_pitch(row, block, port_blocks, row_index, block_index); + row_index++; + } + if (testing_on + && (textord_debug_pitch_test && block->block->text_region () != NULL + || textord_blocksall_fixed || textord_blocksall_prop)) { + tprintf ("Corr:"); + print_block_counts(block, block_index); + } + block_index++; + } +#ifndef GRAPHICS_DISABLED + if (textord_show_initial_words && testing_on) { + overlap_picture_ops(TRUE); + } +#endif +} + + +/********************************************************************** + * fix_row_pitch + * + * Get a pitch_decision for this row by voting among similar rows in the + * block, then similar rows over all the page, or any other rows at all. + **********************************************************************/ + +void fix_row_pitch( //get some value + TO_ROW *bad_row, //row to fix + TO_BLOCK *bad_block, //block of bad_row + TO_BLOCK_LIST *blocks, //blocks to scan + INT32 row_target, //number of row + INT32 block_target //number of block + ) { + const char *res_string; //decision on line + INT16 mid_cuts; + int block_votes; //votes in block + int like_votes; //votes over page + int other_votes; //votes of unlike blocks + int block_index; //number of block + int row_index; //number of row + int maxwidth; //max pitch + TO_BLOCK_IT block_it = blocks; //block iterator + TO_ROW_IT row_it; + TO_BLOCK *block; //current block + TO_ROW *row; //current row + float sp_sd; //space deviation + STATS block_stats; //pitches in block + STATS like_stats; //pitches in page + + block_votes = like_votes = other_votes = 0; + maxwidth = (INT32) ceil (bad_row->xheight * textord_words_maxspace); + if (bad_row->pitch_decision != PITCH_DEF_FIXED + && bad_row->pitch_decision != PITCH_DEF_PROP) { + block_stats.set_range (0, maxwidth); + like_stats.set_range (0, maxwidth); + block_index = 1; + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + row_index = 1; + row_it.set_to_list (block->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); + row_it.forward ()) { + row = row_it.data (); + if (bad_row->all_caps + && row->xheight + row->ascrise + < + (bad_row->xheight + bad_row->ascrise) * (1 + + textord_pitch_rowsimilarity) + && row->xheight + row->ascrise > + (bad_row->xheight + bad_row->ascrise) * (1 - + textord_pitch_rowsimilarity) + || !bad_row->all_caps + && row->xheight < + bad_row->xheight * (1 + textord_pitch_rowsimilarity) + && row->xheight > + bad_row->xheight * (1 - textord_pitch_rowsimilarity)) { + if (block_index == block_target) { + if (row->pitch_decision == PITCH_DEF_FIXED) { + block_votes += textord_words_veto_power; + block_stats.add ((INT32) row->fixed_pitch, + textord_words_veto_power); + } + else if (row->pitch_decision == PITCH_MAYBE_FIXED + || row->pitch_decision == PITCH_CORR_FIXED) { + block_votes++; + block_stats.add ((INT32) row->fixed_pitch, 1); + } + else if (row->pitch_decision == PITCH_DEF_PROP) + block_votes -= textord_words_veto_power; + else if (row->pitch_decision == PITCH_MAYBE_PROP + || row->pitch_decision == PITCH_CORR_PROP) + block_votes--; + } + else { + if (row->pitch_decision == PITCH_DEF_FIXED) { + like_votes += textord_words_veto_power; + like_stats.add ((INT32) row->fixed_pitch, + textord_words_veto_power); + } + else if (row->pitch_decision == PITCH_MAYBE_FIXED + || row->pitch_decision == PITCH_CORR_FIXED) { + like_votes++; + like_stats.add ((INT32) row->fixed_pitch, 1); + } + else if (row->pitch_decision == PITCH_DEF_PROP) + like_votes -= textord_words_veto_power; + else if (row->pitch_decision == PITCH_MAYBE_PROP + || row->pitch_decision == PITCH_CORR_PROP) + like_votes--; + } + } + else { + if (row->pitch_decision == PITCH_DEF_FIXED) + other_votes += textord_words_veto_power; + else if (row->pitch_decision == PITCH_MAYBE_FIXED + || row->pitch_decision == PITCH_CORR_FIXED) + other_votes++; + else if (row->pitch_decision == PITCH_DEF_PROP) + other_votes -= textord_words_veto_power; + else if (row->pitch_decision == PITCH_MAYBE_PROP + || row->pitch_decision == PITCH_CORR_PROP) + other_votes--; + } + row_index++; + } + block_index++; + } + if (block_votes > textord_words_veto_power) { + bad_row->fixed_pitch = block_stats.ile (0.5); + bad_row->pitch_decision = PITCH_CORR_FIXED; + } + else if (block_votes <= textord_words_veto_power && like_votes > 0) { + bad_row->fixed_pitch = like_stats.ile (0.5); + bad_row->pitch_decision = PITCH_CORR_FIXED; + } + else { + bad_row->pitch_decision = PITCH_CORR_PROP; + #ifndef SECURE_NAMES + if (block_votes == 0 && like_votes == 0 && other_votes > 0 + && (textord_debug_pitch_test || textord_debug_pitch_metric)) + tprintf + ("Warning:row %d of block %d set prop with no like rows against trend\n", + row_target, block_target); + #endif + } + } + if (textord_debug_pitch_metric) { + tprintf (":b_votes=%d:l_votes=%d:o_votes=%d", + block_votes, like_votes, other_votes); + if (bad_row->pitch_decision == PITCH_CORR_PROP + || bad_row->pitch_decision == PITCH_DEF_PROP) { + res_string = bad_block->block->text_region () != NULL ? + (bad_block->block->text_region ()-> + is_prop ()? "CP" : "WP") : "XP"; + } + else { + res_string = bad_block->block->text_region () != NULL ? + (bad_block->block->text_region ()-> + is_prop ()? "WF" : "CF") : "XF"; + } + tprintf (":Blk=%d:Row=%d:%c:", + block_target, row_target, + bad_block->block->text_region () != NULL ? + (bad_block->block->text_region ()-> + is_prop ()? 'P' : 'F') : 'X'); + tprintf ("x=%g:asc=%g:corr_res=%s\n", bad_row->xheight, + bad_row->ascrise, res_string); + } + if (textord_pitch_cheat && bad_block->block->text_region () != NULL) + bad_row->pitch_decision = + bad_block->block->text_region ()-> + is_prop ()? PITCH_CORR_PROP : PITCH_CORR_FIXED; + if (bad_row->pitch_decision == PITCH_CORR_FIXED) { + if (bad_row->fixed_pitch < textord_min_xheight) { + if (block_votes > 0) + bad_row->fixed_pitch = block_stats.ile (0.5); + else if (block_votes == 0 && like_votes > 0) + bad_row->fixed_pitch = like_stats.ile (0.5); + else { + tprintf + ("Warning:guessing pitch as xheight on row %d, block %d\n", + row_target, block_target); + bad_row->fixed_pitch = bad_row->xheight; + } + } + if (bad_row->fixed_pitch < textord_min_xheight) + bad_row->fixed_pitch = (float) textord_min_xheight; + bad_row->kern_size = bad_row->fixed_pitch / 4; + bad_row->min_space = (INT32) (bad_row->fixed_pitch * 0.6); + bad_row->max_nonspace = (INT32) (bad_row->fixed_pitch * 0.4); + bad_row->space_threshold = + (bad_row->min_space + bad_row->max_nonspace) / 2; + bad_row->space_size = bad_row->fixed_pitch; + if (bad_row->char_cells.empty ()) + tune_row_pitch (bad_row, &bad_row->projection, + bad_row->projection_left, bad_row->projection_right, + (bad_row->fixed_pitch + + bad_row->max_nonspace * 3) / 4, bad_row->fixed_pitch, + sp_sd, mid_cuts, &bad_row->char_cells, FALSE); + } + else if (bad_row->pitch_decision == PITCH_CORR_PROP + || bad_row->pitch_decision == PITCH_DEF_PROP) { + bad_row->fixed_pitch = 0.0f; + bad_row->char_cells.clear (); + } +} + + +/********************************************************************** + * compute_block_pitch + * + * Decide whether each block is fixed pitch individually. + **********************************************************************/ + +void compute_block_pitch( //process each block + TO_BLOCK *block, //input list + FCOORD rotation, //for drawing + INT32 block_index, //block number + BOOL8 testing_on //correct orientation + ) { + BOX block_box; //bounding box + + block_box = block->block->bounding_box (); + if (testing_on && textord_debug_pitch_test) { + tprintf ("Block %d at (%d,%d)->(%d,%d)\n", + block_index, + block_box.left (), block_box.bottom (), + block_box.right (), block_box.top ()); + } + block->min_space = (INT32) floor (block->xheight + * textord_words_default_minspace); + block->max_nonspace = (INT32) ceil (block->xheight + * textord_words_default_nonspace); + block->fixed_pitch = 0.0f; + block->space_size = (float) block->min_space; + block->kern_size = (float) block->max_nonspace; + block->pr_nonsp = block->xheight * words_default_prop_nonspace; + block->pr_space = block->pr_nonsp * textord_spacesize_ratioprop; + if (!block->get_rows ()->empty ()) { + ASSERT_HOST (block->xheight > 0); + if (textord_repeat_extraction) + find_repeated_chars(block, textord_show_initial_words &&testing_on); +#ifndef GRAPHICS_DISABLED + if (textord_show_initial_words && testing_on) + overlap_picture_ops(TRUE); +#endif + compute_rows_pitch(block, + block_index, + textord_debug_pitch_test &&testing_on); + } +} + + +/********************************************************************** + * compute_rows_pitch + * + * Decide whether each row is fixed pitch individually. + **********************************************************************/ + +BOOL8 compute_rows_pitch( //find line stats + TO_BLOCK *block, //block to do + INT32 block_index, //block number + BOOL8 testing_on //correct orientation + ) { + INT32 maxwidth; //of spaces + TO_ROW *row; //current row + INT32 row_index; //row number. + float lower, upper; //cluster thresholds + TO_ROW_IT row_it = block->get_rows (); + + row_index = 1; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + ASSERT_HOST (row->xheight > 0); + row->compute_vertical_projection (); + maxwidth = (INT32) ceil (row->xheight * textord_words_maxspace); + if (row_pitch_stats (row, maxwidth, testing_on) + && find_row_pitch (row, maxwidth, + textord_dotmatrix_gap + 1, block, block_index, + row_index, testing_on)) { + if (row->fixed_pitch == 0) { + lower = row->pr_nonsp; + upper = row->pr_space; + row->space_size = upper; + row->kern_size = lower; + } + } + else { + row->fixed_pitch = 0.0f; //insufficient data + row->pitch_decision = PITCH_DUNNO; + } + row_index++; + } + return FALSE; +} + + +/********************************************************************** + * try_doc_fixed + * + * Attempt to call the entire document fixed pitch. + **********************************************************************/ + +BOOL8 try_doc_fixed( //determine pitch + ICOORD page_tr, //top right + TO_BLOCK_LIST *port_blocks, //input list + float gradient //page skew + ) { + INT16 master_x; //uniform shifts + INT16 pitch; //median pitch. + int x; //profile coord + int prop_blocks; //correct counts + int fixed_blocks; + int total_row_count; //total in page + //iterator + TO_BLOCK_IT block_it = port_blocks; + TO_BLOCK *block; //current block; + TO_ROW_IT row_it; //row iterator + TO_ROW *row; //current row + INT16 projection_left; //edges + INT16 projection_right; + INT16 row_left; //edges of row + INT16 row_right; + ICOORDELT_LIST *master_cells; //cells for page + float master_y; //uniform shifts + float shift_factor; //page skew correction + float row_shift; //shift for row + float final_pitch; //output pitch + float row_y; //baseline + STATS projection; //entire page + STATS pitches (0, MAX_ALLOWED_PITCH); + //for median + float sp_sd; //space sd + INT16 mid_cuts; //no of cheap cuts + float pitch_sd; //sync rating + + if (block_it.empty () + // || block_it.data()==block_it.data_relative(1) + || !textord_blockndoc_fixed) + return FALSE; + shift_factor = gradient / (gradient * gradient + 1); + row_it.set_to_list (block_it.data ()->get_rows ()); + master_x = row_it.data ()->projection_left; + master_y = row_it.data ()->baseline.y (master_x); + projection_left = MAX_INT16; + projection_right = -MAX_INT16; + prop_blocks = 0; + fixed_blocks = 0; + total_row_count = 0; + + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + if (block->block->text_region () != NULL) { + if (block->block->text_region ()->is_prop ()) + prop_blocks++; + else + fixed_blocks++; + } + row_it.set_to_list (block->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + total_row_count++; + if (row->fixed_pitch > 0) + pitches.add ((INT32) (row->fixed_pitch), 1); + //find median + row_y = row->baseline.y (master_x); + row_left = + (INT16) (row->projection_left - + shift_factor * (master_y - row_y)); + row_right = + (INT16) (row->projection_right - + shift_factor * (master_y - row_y)); + if (row_left < projection_left) + projection_left = row_left; + if (row_right > projection_right) + projection_right = row_right; + } + } + if (pitches.get_total () == 0) + return FALSE; + projection.set_range (projection_left, projection_right); + + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + row_y = row->baseline.y (master_x); + row_left = + (INT16) (row->projection_left - + shift_factor * (master_y - row_y)); + for (x = row->projection_left; x < row->projection_right; + x++, row_left++) { + projection.add (row_left, row->projection.pile_count (x)); + } + } + } + + row_it.set_to_list (block_it.data ()->get_rows ()); + row = row_it.data (); +#ifndef GRAPHICS_DISABLED + if (textord_show_page_cuts && to_win != NO_WINDOW) + projection.plot (to_win, projection_left, + row->intercept (), 1.0f, -1.0f, CORAL); +#endif + final_pitch = pitches.ile (0.5); + pitch = (INT16) final_pitch; + pitch_sd = + tune_row_pitch (row, &projection, projection_left, projection_right, + pitch * 0.75, final_pitch, sp_sd, mid_cuts, + &row->char_cells, FALSE); + + if (textord_debug_pitch_metric) + tprintf + ("try_doc:props=%d:fixed=%d:pitch=%d:final_pitch=%g:pitch_sd=%g:sp_sd=%g:sd/trc=%g:sd/p=%g:sd/trc/p=%g\n", + prop_blocks, fixed_blocks, pitch, final_pitch, pitch_sd, sp_sd, + pitch_sd / total_row_count, pitch_sd / pitch, + pitch_sd / total_row_count / pitch); + +#ifndef GRAPHICS_DISABLED + if (textord_show_page_cuts && to_win != NO_WINDOW) { + master_cells = &row->char_cells; + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); + row_it.forward ()) { + row = row_it.data (); + row_y = row->baseline.y (master_x); + row_shift = shift_factor * (master_y - row_y); + plot_row_cells(to_win, GOLDENROD, row, row_shift, master_cells); + } + } + } +#endif + row->char_cells.clear (); + return FALSE; +} + + +/********************************************************************** + * try_block_fixed + * + * Try to call the entire block fixed. + **********************************************************************/ + +BOOL8 try_block_fixed( //find line stats + TO_BLOCK *block, //block to do + INT32 block_index //block number + ) { + return FALSE; +} + + +/********************************************************************** + * try_rows_fixed + * + * Decide whether each row is fixed pitch individually. + **********************************************************************/ + +BOOL8 try_rows_fixed( //find line stats + TO_BLOCK *block, //block to do + INT32 block_index, //block number + BOOL8 testing_on //correct orientation + ) { + INT32 maxwidth; //of spaces + TO_ROW *row; //current row + INT32 row_index; //row number. + INT32 def_fixed = 0; //counters + INT32 def_prop = 0; + INT32 maybe_fixed = 0; + INT32 maybe_prop = 0; + INT32 dunno = 0; + INT32 corr_fixed = 0; + INT32 corr_prop = 0; + float lower, upper; //cluster thresholds + TO_ROW_IT row_it = block->get_rows (); + + row_index = 1; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + ASSERT_HOST (row->xheight > 0); + maxwidth = (INT32) ceil (row->xheight * textord_words_maxspace); + if (row->fixed_pitch > 0 && fixed_pitch_row (row, block_index)) { + if (row->fixed_pitch == 0) { + lower = row->pr_nonsp; + upper = row->pr_space; + row->space_size = upper; + row->kern_size = lower; + } + } + row_index++; + } + count_block_votes(block, + def_fixed, + def_prop, + maybe_fixed, + maybe_prop, + corr_fixed, + corr_prop, + dunno); + if (testing_on + && (textord_debug_pitch_test + || textord_blocksall_prop || textord_blocksall_fixed)) { + tprintf ("Initially:"); + print_block_counts(block, block_index); + } + if (def_fixed > def_prop * textord_words_veto_power) + block->pitch_decision = PITCH_DEF_FIXED; + else if (def_prop > def_fixed * textord_words_veto_power) + block->pitch_decision = PITCH_DEF_PROP; + else if (def_fixed > 0 || def_prop > 0) + block->pitch_decision = PITCH_DUNNO; + else if (maybe_fixed > maybe_prop * textord_words_veto_power) + block->pitch_decision = PITCH_MAYBE_FIXED; + else if (maybe_prop > maybe_fixed * textord_words_veto_power) + block->pitch_decision = PITCH_MAYBE_PROP; + else + block->pitch_decision = PITCH_DUNNO; + return FALSE; +} + + +/********************************************************************** + * print_block_counts + * + * Count up how many rows have what decision and print the results. + **********************************************************************/ + +void print_block_counts( //find line stats + TO_BLOCK *block, //block to do + INT32 block_index //block number + ) { + INT32 def_fixed = 0; //counters + INT32 def_prop = 0; + INT32 maybe_fixed = 0; + INT32 maybe_prop = 0; + INT32 dunno = 0; + INT32 corr_fixed = 0; + INT32 corr_prop = 0; + + count_block_votes(block, + def_fixed, + def_prop, + maybe_fixed, + maybe_prop, + corr_fixed, + corr_prop, + dunno); + tprintf ("Block %d has (%d,%d,%d)", + block_index, def_fixed, maybe_fixed, corr_fixed); + if ((textord_blocksall_prop + || block->block->text_region () != NULL + && block->block->text_region ()->is_prop ()) && (def_fixed + || maybe_fixed + || corr_fixed)) + tprintf (" (Wrongly)"); + tprintf (" fixed, (%d,%d,%d)", def_prop, maybe_prop, corr_prop); + if ((textord_blocksall_fixed + || block->block->text_region () != NULL + && !block->block->text_region ()->is_prop ()) && (def_prop + || maybe_prop + || corr_prop)) + tprintf (" (Wrongly)"); + tprintf (" prop, %d dunno\n", dunno); +} + + +/********************************************************************** + * count_block_votes + * + * Count the number of rows in the block with each kind of pitch_decision. + **********************************************************************/ + +void count_block_votes( //find line stats + TO_BLOCK *block, //block to do + INT32 &def_fixed, //add to counts + INT32 &def_prop, + INT32 &maybe_fixed, + INT32 &maybe_prop, + INT32 &corr_fixed, + INT32 &corr_prop, + INT32 &dunno) { + TO_ROW *row; //current row + TO_ROW_IT row_it = block->get_rows (); + + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + switch (row->pitch_decision) { + case PITCH_DUNNO: + dunno++; + break; + case PITCH_DEF_PROP: + def_prop++; + break; + case PITCH_MAYBE_PROP: + maybe_prop++; + break; + case PITCH_DEF_FIXED: + def_fixed++; + break; + case PITCH_MAYBE_FIXED: + maybe_fixed++; + break; + case PITCH_CORR_PROP: + corr_prop++; + break; + case PITCH_CORR_FIXED: + corr_fixed++; + break; + } + } +} + + +/********************************************************************** + * row_pitch_stats + * + * Decide whether each row is fixed pitch individually. + **********************************************************************/ + +BOOL8 row_pitch_stats( //find line stats + TO_ROW *row, //current row + INT32 maxwidth, //of spaces + BOOL8 testing_on //correct orientation + ) { + BLOBNBOX *blob; //current blob + int gap_index; //current gap + INT32 prev_x; //end of prev blob + INT32 cluster_count; //no of clusters + INT32 prev_count; //of clusters + INT32 smooth_factor; //for smoothing stats + BOX blob_box; //bounding box + float lower, upper; //cluster thresholds + //gap sizes + float gaps[BLOCK_STATS_CLUSTERS]; + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + STATS gap_stats (0, maxwidth); + STATS cluster_stats[BLOCK_STATS_CLUSTERS + 1]; + //clusters + + smooth_factor = + (INT32) (row->xheight * textord_wordstats_smooth_factor + 1.5); + if (!blob_it.empty ()) { + prev_x = blob_it.data ()->bounding_box ().right (); + blob_it.forward (); + while (!blob_it.at_first ()) { + blob = blob_it.data (); + if (!blob->joined_to_prev ()) { + blob_box = blob->bounding_box (); + if (blob_box.left () - prev_x < maxwidth) + gap_stats.add (blob_box.left () - prev_x, 1); + prev_x = blob_box.right (); + } + blob_it.forward (); + } + } + if (gap_stats.get_total () == 0) { + return FALSE; + } + cluster_count = 0; + lower = row->xheight * words_initial_lower; + upper = row->xheight * words_initial_upper; + gap_stats.smooth (smooth_factor); + do { + prev_count = cluster_count; + cluster_count = gap_stats.cluster (lower, upper, + textord_spacesize_ratioprop, + BLOCK_STATS_CLUSTERS, cluster_stats); + } + while (cluster_count > prev_count && cluster_count < BLOCK_STATS_CLUSTERS); + if (cluster_count < 1) { + return FALSE; + } + for (gap_index = 0; gap_index < cluster_count; gap_index++) + gaps[gap_index] = cluster_stats[gap_index + 1].ile (0.5); + //get medians + if (testing_on) { + tprintf ("cluster_count=%d:", cluster_count); + for (gap_index = 0; gap_index < cluster_count; gap_index++) + tprintf (" %g(%d)", gaps[gap_index], + cluster_stats[gap_index + 1].get_total ()); + tprintf ("\n"); + } + qsort (gaps, cluster_count, sizeof (float), sort_floats2); + + //Try to find proportional non-space and space for row. + lower = row->xheight * words_default_prop_nonspace; + upper = row->xheight * textord_words_min_minspace; + for (gap_index = 0; gap_index < cluster_count + && gaps[gap_index] < lower; gap_index++); + if (gap_index == 0) { + if (testing_on) + tprintf ("No clusters below nonspace threshold!!\n"); + if (cluster_count > 1) { + row->pr_nonsp = gaps[0]; + row->pr_space = gaps[1]; + } + else { + row->pr_nonsp = lower; + row->pr_space = gaps[0]; + } + } + else { + row->pr_nonsp = gaps[gap_index - 1]; + while (gap_index < cluster_count && gaps[gap_index] < upper) + gap_index++; + if (gap_index == cluster_count) { + if (testing_on) + tprintf ("No clusters above nonspace threshold!!\n"); + row->pr_space = lower * textord_spacesize_ratioprop; + } + else + row->pr_space = gaps[gap_index]; + } + + //Now try to find the fixed pitch space and non-space. + upper = row->xheight * words_default_fixed_space; + for (gap_index = 0; gap_index < cluster_count + && gaps[gap_index] < upper; gap_index++); + if (gap_index == 0) { + if (testing_on) + tprintf ("No clusters below space threshold!!\n"); + row->fp_nonsp = upper; + row->fp_space = gaps[0]; + } + else { + row->fp_nonsp = gaps[gap_index - 1]; + if (gap_index == cluster_count) { + if (testing_on) + tprintf ("No clusters above space threshold!!\n"); + row->fp_space = row->xheight; + } + else + row->fp_space = gaps[gap_index]; + } + if (testing_on) { + tprintf + ("Initial estimates:pr_nonsp=%g, pr_space=%g, fp_nonsp=%g, fp_space=%g\n", + row->pr_nonsp, row->pr_space, row->fp_nonsp, row->fp_space); + } + return TRUE; //computed some stats +} + + +/********************************************************************** + * find_row_pitch + * + * Check to see if this row could be fixed pitch using the given spacings. + * Blobs with gaps smaller than the lower threshold are assumed to be one. + * The larger threshold is the word gap threshold. + **********************************************************************/ + +BOOL8 find_row_pitch( //find lines + TO_ROW *row, //row to do + INT32 maxwidth, //max permitted space + INT32 dm_gap, //ignorable gaps + TO_BLOCK *block, //block of row + INT32 block_index, //block_number + INT32 row_index, //number of row + BOOL8 testing_on //correct orientation + ) { + BOOL8 used_dm_model; //looks lik dot matrix + float min_space; //estimate threshold + float non_space; //gap size + float gap_iqr; //interquartile range + float pitch_iqr; + float dm_gap_iqr; //interquartile range + float dm_pitch_iqr; + float dm_pitch; //pitch with dm on + float pitch; //revised estimate + float initial_pitch; //guess at pitch + STATS gap_stats (0, maxwidth); + //centre-centre + STATS pitch_stats (0, maxwidth); + + row->fixed_pitch = 0.0f; + initial_pitch = row->fp_space; + if (initial_pitch > row->xheight * (1 + words_default_fixed_limit)) + initial_pitch = row->xheight;//keep pitch decent + non_space = row->fp_nonsp; + if (non_space > initial_pitch) + non_space = initial_pitch; + min_space = (initial_pitch + non_space) / 2; + + if (!count_pitch_stats (row, &gap_stats, &pitch_stats, + initial_pitch, min_space, TRUE, FALSE, dm_gap)) { + dm_gap_iqr = 0.0001; + dm_pitch_iqr = maxwidth * 2.0f; + dm_pitch = initial_pitch; + } + else { + dm_gap_iqr = gap_stats.ile (0.75) - gap_stats.ile (0.25); + dm_pitch_iqr = pitch_stats.ile (0.75) - pitch_stats.ile (0.25); + dm_pitch = pitch_stats.ile (0.5); + } + gap_stats.clear (); + pitch_stats.clear (); + if (!count_pitch_stats (row, &gap_stats, &pitch_stats, + initial_pitch, min_space, TRUE, FALSE, 0)) { + gap_iqr = 0.0001; + pitch_iqr = maxwidth * 3.0f; + } + else { + gap_iqr = gap_stats.ile (0.75) - gap_stats.ile (0.25); + pitch_iqr = pitch_stats.ile (0.75) - pitch_stats.ile (0.25); + if (testing_on) + tprintf + ("First fp iteration:initial_pitch=%g, gap_iqr=%g, pitch_iqr=%g, pitch=%g\n", + initial_pitch, gap_iqr, pitch_iqr, pitch_stats.ile (0.5)); + initial_pitch = pitch_stats.ile (0.5); + if (min_space > initial_pitch + && count_pitch_stats (row, &gap_stats, &pitch_stats, + initial_pitch, initial_pitch, TRUE, FALSE, 0)) { + min_space = initial_pitch; + gap_iqr = gap_stats.ile (0.75) - gap_stats.ile (0.25); + pitch_iqr = pitch_stats.ile (0.75) - pitch_stats.ile (0.25); + if (testing_on) + tprintf + ("Revised fp iteration:initial_pitch=%g, gap_iqr=%g, pitch_iqr=%g, pitch=%g\n", + initial_pitch, gap_iqr, pitch_iqr, pitch_stats.ile (0.5)); + initial_pitch = pitch_stats.ile (0.5); + } + } + if (textord_debug_pitch_metric) + tprintf ("Blk=%d:Row=%d:%c:p_iqr=%g:g_iqr=%g:dm_p_iqr=%g:dm_g_iqr=%g:%c:", + block_index, row_index, + block->block->text_region () != NULL ? + (block->block->text_region ()->is_prop ()? 'P' : 'F') : 'X', + pitch_iqr, gap_iqr, dm_pitch_iqr, dm_gap_iqr, + pitch_iqr > maxwidth && dm_pitch_iqr > maxwidth ? 'D' + : (pitch_iqr * dm_gap_iqr <= + dm_pitch_iqr * gap_iqr ? 'S' : 'M')); + if (pitch_iqr > maxwidth && dm_pitch_iqr > maxwidth) { + row->pitch_decision = PITCH_DUNNO; + if (textord_debug_pitch_metric) + tprintf ("\n"); + return FALSE; //insufficient data + } + if (pitch_iqr * dm_gap_iqr <= dm_pitch_iqr * gap_iqr) { + if (testing_on) + tprintf + ("Choosing non dm version:pitch_iqr=%g, gap_iqr=%g, dm_pitch_iqr=%g, dm_gap_iqr=%g\n", + pitch_iqr, gap_iqr, dm_pitch_iqr, dm_gap_iqr); + gap_iqr = gap_stats.ile (0.75) - gap_stats.ile (0.25); + pitch_iqr = pitch_stats.ile (0.75) - pitch_stats.ile (0.25); + pitch = pitch_stats.ile (0.5); + used_dm_model = FALSE; + } + else { + if (testing_on) + tprintf + ("Choosing dm version:pitch_iqr=%g, gap_iqr=%g, dm_pitch_iqr=%g, dm_gap_iqr=%g\n", + pitch_iqr, gap_iqr, dm_pitch_iqr, dm_gap_iqr); + gap_iqr = dm_gap_iqr; + pitch_iqr = dm_pitch_iqr; + pitch = dm_pitch; + used_dm_model = TRUE; + } + if (textord_debug_pitch_metric) { + tprintf ("rev_p_iqr=%g:rev_g_iqr=%g:pitch=%g:", + pitch_iqr, gap_iqr, pitch); + tprintf ("p_iqr/g=%g:p_iqr/x=%g:iqr_res=%c:", + pitch_iqr / gap_iqr, pitch_iqr / block->xheight, + pitch_iqr < gap_iqr * textord_fpiqr_ratio + && pitch_iqr < block->xheight * textord_max_pitch_iqr + && pitch < block->xheight * textord_words_default_maxspace + ? 'F' : 'P'); + } + if (pitch_iqr < gap_iqr * textord_fpiqr_ratio + && pitch_iqr < block->xheight * textord_max_pitch_iqr + && pitch < block->xheight * textord_words_default_maxspace) + row->pitch_decision = PITCH_MAYBE_FIXED; + else + row->pitch_decision = PITCH_MAYBE_PROP; + row->fixed_pitch = pitch; + row->kern_size = gap_stats.ile (0.5); + row->min_space = (INT32) (row->fixed_pitch + non_space) / 2; + if (row->min_space > row->fixed_pitch) + row->min_space = (INT32) row->fixed_pitch; + row->max_nonspace = row->min_space; + row->space_size = row->fixed_pitch; + row->space_threshold = (row->max_nonspace + row->min_space) / 2; + row->used_dm_model = used_dm_model; + return TRUE; +} + + +/********************************************************************** + * fixed_pitch_row + * + * Check to see if this row could be fixed pitch using the given spacings. + * Blobs with gaps smaller than the lower threshold are assumed to be one. + * The larger threshold is the word gap threshold. + **********************************************************************/ + +BOOL8 fixed_pitch_row( //find lines + TO_ROW *row, //row to do + INT32 block_index //block_number + ) { + const char *res_string; //pitch result + INT16 mid_cuts; //no of cheap cuts + float non_space; //gap size + float pitch_sd; //error on pitch + float sp_sd; //space sd + + non_space = row->fp_nonsp; + if (non_space > row->fixed_pitch) + non_space = row->fixed_pitch; + if (textord_all_prop) { + // Set the decision to definitely proportional. + pitch_sd = textord_words_def_prop * row->fixed_pitch; + row->pitch_decision = PITCH_DEF_PROP; + } else { + pitch_sd = tune_row_pitch (row, &row->projection, row->projection_left, + row->projection_right, + (row->fixed_pitch + non_space * 3) / 4, + row->fixed_pitch, sp_sd, mid_cuts, + &row->char_cells, + block_index == textord_debug_block); + if (pitch_sd < textord_words_pitchsd_threshold * row->fixed_pitch + && ((pitsync_linear_version & 3) < 3 + || (pitsync_linear_version & 3) >= 3 && (row->used_dm_model + || sp_sd > 20 + || pitch_sd == 0 + && sp_sd > 10))) { + if (pitch_sd < textord_words_def_fixed * row->fixed_pitch + && !row->all_caps + && ((pitsync_linear_version & 3) < 3 || sp_sd > 20)) + row->pitch_decision = PITCH_DEF_FIXED; + else + row->pitch_decision = PITCH_MAYBE_FIXED; + } + else if ((pitsync_linear_version & 3) < 3 + || sp_sd > 20 + || mid_cuts > 0 + || pitch_sd >= textord_words_pitchsd_threshold * row->fixed_pitch) { + if (pitch_sd < textord_words_def_prop * row->fixed_pitch) + row->pitch_decision = PITCH_MAYBE_PROP; + else + row->pitch_decision = PITCH_DEF_PROP; + } + else + row->pitch_decision = PITCH_DUNNO; + } + + if (textord_debug_pitch_metric) { + res_string = "??"; + switch (row->pitch_decision) { + case PITCH_DEF_PROP: + res_string = "DP"; + break; + case PITCH_MAYBE_PROP: + res_string = "MP"; + break; + case PITCH_DEF_FIXED: + res_string = "DF"; + break; + case PITCH_MAYBE_FIXED: + res_string = "MF"; + default: + res_string = "??"; + } + tprintf (":sd/p=%g:occ=%g:init_res=%s\n", + pitch_sd / row->fixed_pitch, sp_sd, res_string); + } + return TRUE; +} + + +/********************************************************************** + * count_pitch_stats + * + * Count up the gap and pitch stats on the block to see if it is fixed pitch. + * Blobs with gaps smaller than the lower threshold are assumed to be one. + * The larger threshold is the word gap threshold. + * The return value indicates whether there were any decent values to use. + **********************************************************************/ + +BOOL8 count_pitch_stats( //find lines + TO_ROW *row, //row to do + STATS *gap_stats, //blob gaps + STATS *pitch_stats, //centre-centre stats + float initial_pitch, //guess at pitch + float min_space, //estimate space size + BOOL8 ignore_outsize, //discard big objects + BOOL8 split_outsize, //split big objects + INT32 dm_gap //ignorable gaps + ) { + BOOL8 prev_valid; //not word broken + BLOBNBOX *blob; //current blob + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + INT32 prev_right; //end of prev blob + INT32 prev_centre; //centre of previous blob + INT32 x_centre; //centre of this blob + INT32 blob_width; //width of blob + INT32 width_units; //no of widths in blob + float width; //blob width + BOX blob_box; //bounding box + BOX joined_box; //of super blob + + gap_stats->clear (); + pitch_stats->clear (); + if (blob_it.empty ()) + return FALSE; + prev_valid = FALSE; + prev_centre = 0; + prev_right = 0; //stop complier warning + joined_box = blob_it.data ()->bounding_box (); + do { + blob_it.forward (); + blob = blob_it.data (); + if (!blob->joined_to_prev ()) { + blob_box = blob->bounding_box (); + if (blob_box.left () - joined_box.right () < dm_gap + && !blob_it.at_first () + || blob->cblob () == NULL && blob->blob () == NULL) + joined_box += blob_box; //merge blobs + else { + blob_width = joined_box.width (); + if (split_outsize) { + width_units = + (INT32) floor ((float) blob_width / initial_pitch + 0.5); + if (width_units < 1) + width_units = 1; + width_units--; + } + else if (ignore_outsize) { + width = (float) blob_width / initial_pitch; + width_units = width < 1 + words_default_fixed_limit + && width > 1 - words_default_fixed_limit ? 0 : -1; + } + else + width_units = 0; //everything in + x_centre = (INT32) (joined_box.left () + + (blob_width - + width_units * initial_pitch) / 2); + if (prev_valid && width_units >= 0) { + // if (width_units>0) + // { + // tprintf("wu=%d, width=%d, xc=%d, adding %d\n", + // width_units,blob_width,x_centre,x_centre-prev_centre); + // } + gap_stats->add (joined_box.left () - prev_right, 1); + pitch_stats->add (x_centre - prev_centre, 1); + } + prev_centre = (INT32) (x_centre + width_units * initial_pitch); + prev_right = joined_box.right (); + prev_valid = blob_box.left () - joined_box.right () < min_space; + prev_valid = prev_valid && width_units >= 0; + joined_box = blob_box; + } + } + } + while (!blob_it.at_first ()); + return gap_stats->get_total () >= 3; +} + + +/********************************************************************** + * tune_row_pitch + * + * Use a dp algorithm to fit the character cells and return the sd of + * the cell size over the row. + **********************************************************************/ + +float tune_row_pitch( //find fp cells + TO_ROW *row, //row to do + STATS *projection, //vertical projection + INT16 projection_left, //edge of projection + INT16 projection_right, //edge of projection + float space_size, //size of blank + float &initial_pitch, //guess at pitch + float &best_sp_sd, //space sd + INT16 &best_mid_cuts, //no of cheap cuts + ICOORDELT_LIST *best_cells, //row cells + BOOL8 testing_on //inidividual words + ) { + int pitch_delta; //offset pitch + INT16 mid_cuts; //cheap cuts + float pitch_sd; //current sd + float best_sd; //best result + float best_pitch; //pitch for best result + float initial_sd; //starting error + float sp_sd; //space sd + ICOORDELT_LIST test_cells; //row cells + ICOORDELT_IT best_it; //start of best list + + if (textord_fast_pitch_test) + return tune_row_pitch2 (row, projection, projection_left, + projection_right, space_size, initial_pitch, + best_sp_sd, + //space sd + best_mid_cuts, best_cells, testing_on); + if (textord_disable_pitch_test) { + best_sp_sd = initial_pitch; + return initial_pitch; + } + initial_sd = + compute_pitch_sd(row, + projection, + projection_left, + projection_right, + space_size, + initial_pitch, + best_sp_sd, + best_mid_cuts, + best_cells, + testing_on); + best_sd = initial_sd; + best_pitch = initial_pitch; + if (testing_on) + tprintf ("tune_row_pitch:start pitch=%g, sd=%g\n", best_pitch, best_sd); + for (pitch_delta = 1; pitch_delta <= textord_pitch_range; pitch_delta++) { + pitch_sd = + compute_pitch_sd (row, projection, projection_left, projection_right, + space_size, initial_pitch + pitch_delta, sp_sd, + mid_cuts, &test_cells, testing_on); + if (testing_on) + tprintf ("testing pitch at %g, sd=%g\n", initial_pitch + pitch_delta, + pitch_sd); + if (pitch_sd < best_sd) { + best_sd = pitch_sd; + best_mid_cuts = mid_cuts; + best_sp_sd = sp_sd; + best_pitch = initial_pitch + pitch_delta; + best_cells->clear (); + best_it.set_to_list (best_cells); + best_it.add_list_after (&test_cells); + } + else + test_cells.clear (); + if (pitch_sd > initial_sd) + break; //getting worse + } + for (pitch_delta = 1; pitch_delta <= textord_pitch_range; pitch_delta++) { + pitch_sd = + compute_pitch_sd (row, projection, projection_left, projection_right, + space_size, initial_pitch - pitch_delta, sp_sd, + mid_cuts, &test_cells, testing_on); + if (testing_on) + tprintf ("testing pitch at %g, sd=%g\n", initial_pitch - pitch_delta, + pitch_sd); + if (pitch_sd < best_sd) { + best_sd = pitch_sd; + best_mid_cuts = mid_cuts; + best_sp_sd = sp_sd; + best_pitch = initial_pitch - pitch_delta; + best_cells->clear (); + best_it.set_to_list (best_cells); + best_it.add_list_after (&test_cells); + } + else + test_cells.clear (); + if (pitch_sd > initial_sd) + break; + } + initial_pitch = best_pitch; + + if (textord_debug_pitch_metric) + print_pitch_sd(row, + projection, + projection_left, + projection_right, + space_size, + best_pitch); + + return best_sd; +} + + +/********************************************************************** + * tune_row_pitch + * + * Use a dp algorithm to fit the character cells and return the sd of + * the cell size over the row. + **********************************************************************/ + +float tune_row_pitch2( //find fp cells + TO_ROW *row, //row to do + STATS *projection, //vertical projection + INT16 projection_left, //edge of projection + INT16 projection_right, //edge of projection + float space_size, //size of blank + float &initial_pitch, //guess at pitch + float &best_sp_sd, //space sd + INT16 &best_mid_cuts, //no of cheap cuts + ICOORDELT_LIST *best_cells, //row cells + BOOL8 testing_on //inidividual words + ) { + int pitch_delta; //offset pitch + INT16 pixel; //pixel coord + INT16 best_pixel; //pixel coord + INT16 best_delta; //best pitch + INT16 best_pitch; //best pitch + INT16 start; //of good range + INT16 end; //of good range + INT32 best_count; //lowest sum + float best_sd; //best result + STATS *sum_proj; //summed projection + + best_sp_sd = initial_pitch; + + if (textord_disable_pitch_test) { + return initial_pitch; + } + sum_proj = new STATS[textord_pitch_range * 2 + 1]; + if (sum_proj == NULL) + return initial_pitch; + best_pitch = (INT32) initial_pitch; + + for (pitch_delta = -textord_pitch_range; pitch_delta <= textord_pitch_range; + pitch_delta++) + sum_proj[textord_pitch_range + pitch_delta].set_range (0, + best_pitch + + pitch_delta + 1); + for (pixel = projection_left; pixel <= projection_right; pixel++) { + for (pitch_delta = -textord_pitch_range; + pitch_delta <= textord_pitch_range; pitch_delta++) + sum_proj[textord_pitch_range + + pitch_delta].add ((pixel - projection_left) % (best_pitch + + pitch_delta), + projection->pile_count (pixel)); + } + best_count = sum_proj[textord_pitch_range].pile_count (0); + best_delta = 0; + best_pixel = 0; + for (pitch_delta = -textord_pitch_range; pitch_delta <= textord_pitch_range; + pitch_delta++) { + for (pixel = 0; pixel < best_pitch + pitch_delta; pixel++) { + if (sum_proj[textord_pitch_range + pitch_delta].pile_count (pixel) + < best_count) { + best_count = + sum_proj[textord_pitch_range + + pitch_delta].pile_count (pixel); + best_delta = pitch_delta; + best_pixel = pixel; + } + } + } + if (testing_on) + tprintf ("tune_row_pitch:start pitch=%g, best_delta=%d, count=%d\n", + initial_pitch, best_delta, best_count); + best_pitch += best_delta; + initial_pitch = best_pitch; + best_count++; + best_count += best_count; + for (start = best_pixel - 2; start > best_pixel - best_pitch + && sum_proj[textord_pitch_range + + best_delta].pile_count (start % best_pitch) <= best_count; + start--); + for (end = best_pixel + 2; + end < best_pixel + best_pitch + && sum_proj[textord_pitch_range + + best_delta].pile_count (end % best_pitch) <= best_count; + end++); + + best_sd = + compute_pitch_sd(row, + projection, + projection_left, + projection_right, + space_size, + initial_pitch, + best_sp_sd, + best_mid_cuts, + best_cells, + testing_on, + start, + end); + if (testing_on) + tprintf ("tune_row_pitch:output pitch=%g, sd=%g\n", initial_pitch, + best_sd); + + if (textord_debug_pitch_metric) + print_pitch_sd(row, + projection, + projection_left, + projection_right, + space_size, + initial_pitch); + + delete[]sum_proj; + + return best_sd; +} + + +/********************************************************************** + * compute_pitch_sd + * + * Use a dp algorithm to fit the character cells and return the sd of + * the cell size over the row. + **********************************************************************/ + +float compute_pitch_sd( //find fp cells + TO_ROW *row, //row to do + STATS *projection, //vertical projection + INT16 projection_left, //edge + INT16 projection_right, //edge + float space_size, //size of blank + float initial_pitch, //guess at pitch + float &sp_sd, //space sd + INT16 &mid_cuts, //no of free cuts + ICOORDELT_LIST *row_cells, //list of chop pts + BOOL8 testing_on, //inidividual words + INT16 start, //start of good range + INT16 end //end of good range + ) { + INT16 occupation; //no of cells in word. + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + BLOBNBOX_IT start_it; //start of word + BLOBNBOX_IT plot_it; //for plotting + INT16 blob_count; //no of blobs + BOX blob_box; //bounding box + BOX prev_box; //of super blob + INT32 prev_right; //of word sync + int scale_factor; //on scores for big words + INT32 sp_count; //spaces + FPSEGPT_LIST seg_list; //char cells + FPSEGPT_IT seg_it; //iterator + INT16 segpos; //position of segment + INT16 cellpos; //previous cell boundary + //iterator + ICOORDELT_IT cell_it = row_cells; + ICOORDELT *cell; //new cell + double sqsum; //sum of squares + double spsum; //of spaces + double sp_var; //space error + double word_sync; //result for word + INT32 total_count; //total blobs + + if ((pitsync_linear_version & 3) > 1) { + word_sync = compute_pitch_sd2 (row, projection, projection_left, + projection_right, initial_pitch, + occupation, mid_cuts, row_cells, + testing_on, start, end); + sp_sd = occupation; + return word_sync; + } + mid_cuts = 0; + cellpos = 0; + total_count = 0; + sqsum = 0; + sp_count = 0; + spsum = 0; + prev_right = -1; + if (blob_it.empty ()) + return space_size * 10; +#ifndef GRAPHICS_DISABLED + if (testing_on && to_win > 0) { + blob_box = blob_it.data ()->bounding_box (); + projection->plot (to_win, projection_left, + row->intercept (), 1.0f, -1.0f, CORAL); + } +#endif + start_it = blob_it; + blob_count = 0; + blob_box = box_next (&blob_it);//first blob + blob_it.mark_cycle_pt (); + do { + for (; blob_count > 0; blob_count--) + box_next(&start_it); + do { + prev_box = blob_box; + blob_count++; + blob_box = box_next (&blob_it); + } + while (!blob_it.cycled_list () + && blob_box.left () - prev_box.right () < space_size); + plot_it = start_it; + if (pitsync_linear_version & 3) + word_sync = + check_pitch_sync2 (&start_it, blob_count, (INT16) initial_pitch, 2, + projection, projection_left, projection_right, + row->xheight * textord_projection_scale, + occupation, &seg_list, start, end); + else + word_sync = + check_pitch_sync (&start_it, blob_count, (INT16) initial_pitch, 2, + projection, &seg_list); + if (testing_on) { + tprintf ("Word ending at (%d,%d), len=%d, sync rating=%g, ", + prev_box.right (), prev_box.top (), + seg_list.length () - 1, word_sync); + seg_it.set_to_list (&seg_list); + for (seg_it.mark_cycle_pt (); !seg_it.cycled_list (); + seg_it.forward ()) { + if (seg_it.data ()->faked) + tprintf ("(F)"); + tprintf ("%d, ", seg_it.data ()->position ()); + // tprintf("C=%g, s=%g, sq=%g\n", + // seg_it.data()->cost_function(), + // seg_it.data()->sum(), + // seg_it.data()->squares()); + } + tprintf ("\n"); + } +#ifndef GRAPHICS_DISABLED + if (textord_show_fixed_cuts && blob_count > 0 && to_win > 0) + plot_fp_cells2(to_win, GOLDENROD, row, &seg_list); +#endif + seg_it.set_to_list (&seg_list); + if (prev_right >= 0) { + sp_var = seg_it.data ()->position () - prev_right; + sp_var -= floor (sp_var / initial_pitch + 0.5) * initial_pitch; + sp_var *= sp_var; + spsum += sp_var; + sp_count++; + } + for (seg_it.mark_cycle_pt (); !seg_it.cycled_list (); seg_it.forward ()) { + segpos = seg_it.data ()->position (); + if (cell_it.empty () || segpos > cellpos + initial_pitch / 2) { + //big gap + while (!cell_it.empty () && segpos > cellpos + initial_pitch * 3 / 2) { + cell = new ICOORDELT (cellpos + (INT16) initial_pitch, 0); + cell_it.add_after_then_move (cell); + cellpos += (INT16) initial_pitch; + } + //make new one + cell = new ICOORDELT (segpos, 0); + cell_it.add_after_then_move (cell); + cellpos = segpos; + } + else if (segpos > cellpos - initial_pitch / 2) { + cell = cell_it.data (); + //average positions + cell->set_x ((cellpos + segpos) / 2); + cellpos = cell->x (); + } + } + seg_it.move_to_last (); + prev_right = seg_it.data ()->position (); + if (textord_pitch_scalebigwords) { + scale_factor = (seg_list.length () - 2) / 2; + if (scale_factor < 1) + scale_factor = 1; + } + else + scale_factor = 1; + sqsum += word_sync * scale_factor; + total_count += (seg_list.length () - 1) * scale_factor; + seg_list.clear (); + } + while (!blob_it.cycled_list ()); + sp_sd = sp_count > 0 ? sqrt (spsum / sp_count) : 0; + return total_count > 0 ? sqrt (sqsum / total_count) : space_size * 10; +} + + +/********************************************************************** + * compute_pitch_sd2 + * + * Use a dp algorithm to fit the character cells and return the sd of + * the cell size over the row. + **********************************************************************/ + +float compute_pitch_sd2( //find fp cells + TO_ROW *row, //row to do + STATS *projection, //vertical projection + INT16 projection_left, //edge + INT16 projection_right, //edge + float initial_pitch, //guess at pitch + INT16 &occupation, //no of occupied cells + INT16 &mid_cuts, //no of free cuts + ICOORDELT_LIST *row_cells, //list of chop pts + BOOL8 testing_on, //inidividual words + INT16 start, //start of good range + INT16 end //end of good range + ) { + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + BLOBNBOX_IT plot_it; + INT16 blob_count; //no of blobs + BOX blob_box; //bounding box + FPSEGPT_LIST seg_list; //char cells + FPSEGPT_IT seg_it; //iterator + INT16 segpos; //position of segment + //iterator + ICOORDELT_IT cell_it = row_cells; + ICOORDELT *cell; //new cell + double word_sync; //result for word + + mid_cuts = 0; + if (blob_it.empty ()) { + occupation = 0; + return initial_pitch * 10; + } +#ifndef GRAPHICS_DISABLED + if (testing_on && to_win > 0) { + projection->plot (to_win, projection_left, + row->intercept (), 1.0f, -1.0f, CORAL); + } +#endif + blob_count = 0; + blob_it.mark_cycle_pt (); + do { + //first blob + blob_box = box_next (&blob_it); + blob_count++; + } + while (!blob_it.cycled_list ()); + plot_it = blob_it; + word_sync = check_pitch_sync2 (&blob_it, blob_count, (INT16) initial_pitch, + 2, projection, projection_left, + projection_right, + row->xheight * textord_projection_scale, + occupation, &seg_list, start, end); + if (testing_on) { + tprintf ("Row ending at (%d,%d), len=%d, sync rating=%g, ", + blob_box.right (), blob_box.top (), + seg_list.length () - 1, word_sync); + seg_it.set_to_list (&seg_list); + for (seg_it.mark_cycle_pt (); !seg_it.cycled_list (); seg_it.forward ()) { + if (seg_it.data ()->faked) + tprintf ("(F)"); + tprintf ("%d, ", seg_it.data ()->position ()); + // tprintf("C=%g, s=%g, sq=%g\n", + // seg_it.data()->cost_function(), + // seg_it.data()->sum(), + // seg_it.data()->squares()); + } + tprintf ("\n"); + } +#ifndef GRAPHICS_DISABLED + if (textord_show_fixed_cuts && blob_count > 0 && to_win > 0) + plot_fp_cells2(to_win, GOLDENROD, row, &seg_list); +#endif + seg_it.set_to_list (&seg_list); + for (seg_it.mark_cycle_pt (); !seg_it.cycled_list (); seg_it.forward ()) { + segpos = seg_it.data ()->position (); + //make new one + cell = new ICOORDELT (segpos, 0); + cell_it.add_after_then_move (cell); + if (seg_it.at_last ()) + mid_cuts = seg_it.data ()->cheap_cuts (); + } + seg_list.clear (); + return occupation > 0 ? sqrt (word_sync / occupation) : initial_pitch * 10; +} + + +/********************************************************************** + * print_pitch_sd + * + * Use a dp algorithm to fit the character cells and return the sd of + * the cell size over the row. + **********************************************************************/ + +void print_pitch_sd( //find fp cells + TO_ROW *row, //row to do + STATS *projection, //vertical projection + INT16 projection_left, //edges //size of blank + INT16 projection_right, + float space_size, + float initial_pitch //guess at pitch + ) { + const char *res2; //pitch result + INT16 occupation; //used cells + float sp_sd; //space sd + //blobs + BLOBNBOX_IT blob_it = row->blob_list (); + BLOBNBOX_IT start_it; //start of word + BLOBNBOX_IT row_start; //start of row + INT16 blob_count; //no of blobs + INT16 total_blob_count; //total blobs in line + BOX blob_box; //bounding box + BOX prev_box; //of super blob + INT32 prev_right; //of word sync + int scale_factor; //on scores for big words + INT32 sp_count; //spaces + FPSEGPT_LIST seg_list; //char cells + FPSEGPT_IT seg_it; //iterator + double sqsum; //sum of squares + double spsum; //of spaces + double sp_var; //space error + double word_sync; //result for word + double total_count; //total cuts + + if (blob_it.empty ()) + return; + row_start = blob_it; + total_blob_count = 0; + + total_count = 0; + sqsum = 0; + sp_count = 0; + spsum = 0; + prev_right = -1; + blob_it = row_start; + start_it = blob_it; + blob_count = 0; + blob_box = box_next (&blob_it);//first blob + blob_it.mark_cycle_pt (); + do { + for (; blob_count > 0; blob_count--) + box_next(&start_it); + do { + prev_box = blob_box; + blob_count++; + blob_box = box_next (&blob_it); + } + while (!blob_it.cycled_list () + && blob_box.left () - prev_box.right () < space_size); + word_sync = + check_pitch_sync2 (&start_it, blob_count, (INT16) initial_pitch, 2, + projection, projection_left, projection_right, + row->xheight * textord_projection_scale, + occupation, &seg_list, 0, 0); + total_blob_count += blob_count; + seg_it.set_to_list (&seg_list); + if (prev_right >= 0) { + sp_var = seg_it.data ()->position () - prev_right; + sp_var -= floor (sp_var / initial_pitch + 0.5) * initial_pitch; + sp_var *= sp_var; + spsum += sp_var; + sp_count++; + } + seg_it.move_to_last (); + prev_right = seg_it.data ()->position (); + if (textord_pitch_scalebigwords) { + scale_factor = (seg_list.length () - 2) / 2; + if (scale_factor < 1) + scale_factor = 1; + } + else + scale_factor = 1; + sqsum += word_sync * scale_factor; + total_count += (seg_list.length () - 1) * scale_factor; + seg_list.clear (); + } + while (!blob_it.cycled_list ()); + sp_sd = sp_count > 0 ? sqrt (spsum / sp_count) : 0; + word_sync = total_count > 0 ? sqrt (sqsum / total_count) : space_size * 10; + tprintf ("new_sd=%g:sd/p=%g:new_sp_sd=%g:res=%c:", + word_sync, word_sync / initial_pitch, sp_sd, + word_sync < textord_words_pitchsd_threshold * initial_pitch + ? 'F' : 'P'); + + start_it = row_start; + blob_it = row_start; + word_sync = + check_pitch_sync2 (&blob_it, total_blob_count, (INT16) initial_pitch, 2, + projection, projection_left, projection_right, + row->xheight * textord_projection_scale, occupation, + &seg_list, 0, 0); + if (occupation > 1) + word_sync /= occupation; + word_sync = sqrt (word_sync); + +#ifndef GRAPHICS_DISABLED + if (textord_show_row_cuts && to_win != NO_WINDOW) + plot_fp_cells2(to_win, CORAL, row, &seg_list); +#endif + seg_list.clear (); + if (word_sync < textord_words_pitchsd_threshold * initial_pitch) { + if (word_sync < textord_words_def_fixed * initial_pitch + && !row->all_caps) + res2 = "DF"; + else + res2 = "MF"; + } + else + res2 = word_sync < textord_words_def_prop * initial_pitch ? "MP" : "DP"; + tprintf + ("row_sd=%g:sd/p=%g:res=%c:N=%d:res2=%s,init pitch=%g, row_pitch=%g, all_caps=%d\n", + word_sync, word_sync / initial_pitch, + word_sync < textord_words_pitchsd_threshold * initial_pitch ? 'F' : 'P', + occupation, res2, initial_pitch, row->fixed_pitch, row->all_caps); +} + + +/********************************************************************** + * sort_floats + * + * qsort function to sort 2 floats. + **********************************************************************/ + +int sort_floats2( //qsort function + const void *arg1, //ptrs to floats + const void *arg2) { + float diff; //difference + + diff = *((float *) arg1) - *((float *) arg2); + if (diff > 0) + return 1; + else if (diff < 0) + return -1; + else + return 0; +} + + +/********************************************************************** + * find_repeated_chars + * + * Find 4 or more adjacent chars which are the same and put them + * into words in advance of fixed pitch checking and word generation. + **********************************************************************/ + +void find_repeated_chars( //search for equal chars + TO_BLOCK *block, //block to search + BOOL8 testing_on //dbug mode + ) { + BOOL8 bol; //start of line + TO_ROW *row; //current row + TO_ROW_IT row_it = block->get_rows (); + ROW *real_row; //output row + WERD_IT word_it; //new words + WERD *word; //new word + BLOBNBOX *bblob; //current blob + BLOBNBOX *nextblob; //neighbour to compare + BLOBNBOX_IT box_it; //iterator + BLOBNBOX_IT search_it; //forward search + INT32 blobcount; //no of neighbours + INT32 matched_blobcount; //no of matches + INT32 blobindex; //in row + INT32 row_length; //blobs in row + INT32 width_change; //max width change + INT32 blob_width; //required blob width + INT32 space_width; //required gap width + INT32 prev_right; //right edge of last blob + float rating; //match rating + PBLOB *pblob1; //polygonal blob + PBLOB *pblob2; //second blob + BOX word_box; //for plotting + + if (row_it.empty ()) + return; //empty block + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + box_it.set_to_list (row->blob_list ()); + row_length = row->blob_list ()->length (); + blobindex = 0; + word_it.set_to_list (&row->rep_words); + bol = TRUE; + if (!box_it.empty ()) { + real_row = new ROW (row, + (INT16) block->kern_size, + (INT16) block->space_size); + do { + bblob = box_it.data (); + blobcount = 1; + search_it = box_it; + search_it.forward (); + matched_blobcount = 1; + width_change = MAX_INT16; + blob_width = 0; + space_width = 0; + prev_right = bblob->bounding_box ().right (); + if (bblob->bounding_box ().height () * 2 < row->xheight + && !bblob->joined_to_prev () + && (bblob->blob () != NULL || bblob->cblob () != NULL)) { + if (bblob->cblob () != NULL) + pblob1 = new PBLOB (bblob->cblob (), row->xheight); + else + pblob1 = bblob->blob (); + + rating = 0.0f; + while (rating < textord_repeat_rating + && blobindex + blobcount < row_length + && ((nextblob = search_it.data ())->blob () != NULL + || nextblob->cblob () != NULL) + && nextblob->bounding_box ().height () * 2 < + row->xheight) { + if (blobcount == 1) { + space_width = nextblob->bounding_box ().left () + - bblob->bounding_box ().right (); + blob_width = bblob->bounding_box ().width (); + width_change = + blob_width > + space_width ? blob_width : space_width; + width_change = + (INT32) (width_change * + textord_repch_width_variance); + if (width_change < 3) + width_change = 3; + } + if (nextblob->bounding_box ().width () > + blob_width + width_change + || nextblob->bounding_box ().width () < + blob_width - width_change + || nextblob->bounding_box ().left () - prev_right > + space_width + width_change + || nextblob->bounding_box ().left () - prev_right < + space_width - width_change) { + if (testing_on) + tprintf + ("Repch terminated:bw=%d, sw=%d, wc=%d, pr=%d, nb=(%d,%d)\n", + blob_width, space_width, width_change, + prev_right, nextblob->bounding_box ().left (), + nextblob->bounding_box ().right ()); + break; //not good enough + } + if (nextblob->blob () != NULL) + rating = compare_blobs (pblob1, real_row, + nextblob->blob (), real_row); + else { + pblob2 = + new PBLOB (nextblob->cblob (), row->xheight); + rating = + compare_blobs(pblob1, real_row, pblob2, real_row); + delete pblob2; + } + if (rating < textord_repeat_rating) { + // if (testing_on) + // tprintf("Blob at (%d,%d)->(%d,%d) had rating %g\n", + // nextblob->bounding_box().left(), + // nextblob->bounding_box().bottom(), + // nextblob->bounding_box().right(), + // nextblob->bounding_box().top(), + // rating); + blobcount++; + search_it.forward (); + matched_blobcount++; + while (blobindex + blobcount < row_length + && search_it.data ()->joined_to_prev ()) { + search_it.forward (); + blobcount++; //suck in joined bits + } + } + prev_right = nextblob->bounding_box ().right (); + } + if (bblob->cblob () != NULL) + delete pblob1; + + if (matched_blobcount >= textord_repeat_threshold) { + word = + make_real_word (&box_it, blobcount, bol, FALSE, FALSE, + 1); +#ifndef GRAPHICS_DISABLED + if (testing_on) { + word_box = word->bounding_box (); + tprintf + ("Found repeated word of %d blobs (%d matched) from (%d,%d)->(%d,%d)\n", + blobcount, matched_blobcount, word_box.left (), + word_box.bottom (), word_box.right (), + word_box.top ()); + perimeter_color_index(to_win, RED); + interior_style(to_win, INT_HOLLOW, TRUE); + rectangle (to_win, word_box.left (), + word_box.bottom (), word_box.right (), + word_box.top ()); + } +#endif + word->set_flag (W_REP_CHAR, TRUE); + word->set_flag (W_DONT_CHOP, TRUE); + word_it.add_after_then_move (word); + blobindex += blobcount; + } + } + bol = FALSE; + box_it.forward (); //next one + blobindex++; + } + //until all done + while (!box_it.at_first ()); + delete real_row; + } + } +} + + +/********************************************************************** + * plot_fp_word + * + * Plot a block of words as if fixed pitch. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void plot_fp_word( //draw block of words + TO_BLOCK *block, //block to draw + float pitch, //pitch to draw with + float nonspace //for space threshold + ) { + TO_ROW *row; //current row + TO_ROW_IT row_it = block->get_rows (); + + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + row->min_space = (INT32) ((pitch + nonspace) / 2); + row->max_nonspace = row->min_space; + row->space_threshold = row->min_space; + plot_word_decisions (to_win, (INT16) pitch, row); + } +} +#endif diff --git a/textord/topitch.h b/textord/topitch.h new file mode 100644 index 0000000000..8b16f3a3be --- /dev/null +++ b/textord/topitch.h @@ -0,0 +1,195 @@ +/********************************************************************** + * File: topitch.h (Formerly to_pitch.h) + * Description: Code to determine fixed pitchness and the pitch if fixed. + * Author: Ray Smith + * Created: Tue Aug 24 16:57:29 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TOPITCH_H +#define TOPITCH_H + +#include "blobbox.h" +#include "notdll.h" + +extern BOOL_VAR_H (textord_debug_pitch_test, FALSE, +"Debug on fixed pitch test"); +extern BOOL_VAR_H (textord_debug_pitch_metric, FALSE, +"Write full metric stuff"); +extern BOOL_VAR_H (textord_show_row_cuts, FALSE, "Draw row-level cuts"); +extern BOOL_VAR_H (textord_show_page_cuts, FALSE, "Draw page-level cuts"); +extern BOOL_VAR_H (textord_pitch_cheat, FALSE, +"Use correct answer for fixed/prop"); +extern BOOL_VAR_H (textord_blockndoc_fixed, TRUE, +"Attempt whole doc/block fixed pitch"); +extern BOOL_VAR_H (textord_fast_pitch_test, FALSE, +"Do even faster pitch algorithm"); +extern double_VAR_H (textord_projection_scale, 0.125, +"Ding rate for mid-cuts"); +extern double_VAR_H (textord_balance_factor, 2.0, +"Ding rate for unbalanced char cells"); +extern double_VAR_H (textord_repch_width_variance, 0.2, +"Max width change of gap/blob"); +void compute_fixed_pitch( //determine pitch + ICOORD page_tr, //top right + TO_BLOCK_LIST *port_blocks, //input list + float gradient, //page skew + FCOORD rotation, //for drawing + BOOL8 testing_on //correct orientation + ); +void fix_row_pitch( //get some value + TO_ROW *bad_row, //row to fix + TO_BLOCK *bad_block, //block of bad_row + TO_BLOCK_LIST *blocks, //blocks to scan + INT32 row_target, //number of row + INT32 block_target //number of block + ); +void compute_block_pitch( //process each block + TO_BLOCK *block, //input list + FCOORD rotation, //for drawing + INT32 block_index, //block number + BOOL8 testing_on //correct orientation + ); +BOOL8 compute_rows_pitch( //find line stats + TO_BLOCK *block, //block to do + INT32 block_index, //block number + BOOL8 testing_on //correct orientation + ); +BOOL8 try_doc_fixed( //determine pitch + ICOORD page_tr, //top right + TO_BLOCK_LIST *port_blocks, //input list + float gradient //page skew + ); +BOOL8 try_block_fixed( //find line stats + TO_BLOCK *block, //block to do + INT32 block_index //block number + ); +BOOL8 try_rows_fixed( //find line stats + TO_BLOCK *block, //block to do + INT32 block_index, //block number + BOOL8 testing_on //correct orientation + ); +void print_block_counts( //find line stats + TO_BLOCK *block, //block to do + INT32 block_index //block number + ); +void count_block_votes( //find line stats + TO_BLOCK *block, //block to do + INT32 &def_fixed, //add to counts + INT32 &def_prop, + INT32 &maybe_fixed, + INT32 &maybe_prop, + INT32 &corr_fixed, + INT32 &corr_prop, + INT32 &dunno); +BOOL8 row_pitch_stats( //find line stats + TO_ROW *row, //current row + INT32 maxwidth, //of spaces + BOOL8 testing_on //correct orientation + ); +BOOL8 find_row_pitch( //find lines + TO_ROW *row, //row to do + INT32 maxwidth, //max permitted space + INT32 dm_gap, //ignorable gaps + TO_BLOCK *block, //block of row + INT32 block_index, //block_number + INT32 row_index, //number of row + BOOL8 testing_on //correct orientation + ); +BOOL8 fixed_pitch_row( //find lines + TO_ROW *row, //row to do + INT32 block_index //block_number + ); +BOOL8 count_pitch_stats( //find lines + TO_ROW *row, //row to do + STATS *gap_stats, //blob gaps + STATS *pitch_stats, //centre-centre stats + float initial_pitch, //guess at pitch + float min_space, //estimate space size + BOOL8 ignore_outsize, //discard big objects + BOOL8 split_outsize, //split big objects + INT32 dm_gap //ignorable gaps + ); +float tune_row_pitch( //find fp cells + TO_ROW *row, //row to do + STATS *projection, //vertical projection + INT16 projection_left, //edge of projection + INT16 projection_right, //edge of projection + float space_size, //size of blank + float &initial_pitch, //guess at pitch + float &best_sp_sd, //space sd + INT16 &best_mid_cuts, //no of cheap cuts + ICOORDELT_LIST *best_cells, //row cells + BOOL8 testing_on //inidividual words + ); +float tune_row_pitch2( //find fp cells + TO_ROW *row, //row to do + STATS *projection, //vertical projection + INT16 projection_left, //edge of projection + INT16 projection_right, //edge of projection + float space_size, //size of blank + float &initial_pitch, //guess at pitch + float &best_sp_sd, //space sd + INT16 &best_mid_cuts, //no of cheap cuts + ICOORDELT_LIST *best_cells, //row cells + BOOL8 testing_on //inidividual words + ); +float compute_pitch_sd ( //find fp cells +TO_ROW * row, //row to do +STATS * projection, //vertical projection +INT16 projection_left, //edge +INT16 projection_right, //edge +float space_size, //size of blank +float initial_pitch, //guess at pitch +float &sp_sd, //space sd +INT16 & mid_cuts, //no of free cuts +ICOORDELT_LIST * row_cells, //list of chop pts +BOOL8 testing_on, //inidividual words +INT16 start = 0, //start of good range +INT16 end = 0 //end of good range +); +float compute_pitch_sd2 ( //find fp cells +TO_ROW * row, //row to do +STATS * projection, //vertical projection +INT16 projection_left, //edge +INT16 projection_right, //edge +float initial_pitch, //guess at pitch +INT16 & occupation, //no of occupied cells +INT16 & mid_cuts, //no of free cuts +ICOORDELT_LIST * row_cells, //list of chop pts +BOOL8 testing_on, //inidividual words +INT16 start = 0, //start of good range +INT16 end = 0 //end of good range +); +void print_pitch_sd( //find fp cells + TO_ROW *row, //row to do + STATS *projection, //vertical projection + INT16 projection_left, //edges //size of blank + INT16 projection_right, + float space_size, + float initial_pitch //guess at pitch + ); +int sort_floats2( //qsort function + const void *arg1, //ptrs to floats + const void *arg2); +void find_repeated_chars( //search for equal chars + TO_BLOCK *block, //block to search + BOOL8 testing_on //dbug mode + ); +void plot_fp_word( //draw block of words + TO_BLOCK *block, //block to draw + float pitch, //pitch to draw with + float nonspace //for space threshold + ); +#endif diff --git a/textord/tordmain.cpp b/textord/tordmain.cpp new file mode 100644 index 0000000000..bdde2fafea --- /dev/null +++ b/textord/tordmain.cpp @@ -0,0 +1,902 @@ +/********************************************************************** + * File: tordmain.cpp (Formerly textordp.c) + * Description: C++ top level textord code. + * Author: Ray Smith + * Created: Tue Jul 28 17:12:33 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include "stderr.h" +#include "globaloc.h" +#include "tessout.h" +#include "blread.h" +#include "blobbox.h" +//#include "lmedsq.h" +#include "edgblob.h" +//#include "adthsh.h" +#include "drawtord.h" +#include "makerow.h" +#include "wordseg.h" +#include "ocrclass.h" +#include "genblob.h" +#include "imgs.h" +//#include "bairdskw.h" +#include "tordmain.h" +#include "secname.h" + +const ERRCODE BLOCKLESS_BLOBS = "Warning:some blobs assigned to no block"; + +#ifdef GRAPHICS_DISABLED +ETEXT_DESC *global_monitor = NULL; +#endif + +#define EXTERN + +EXTERN BOOL_VAR (textord_show_blobs, FALSE, "Display unsorted blobs"); +EXTERN BOOL_VAR (textord_show_boxes, FALSE, "Display unsorted blobs"); +EXTERN BOOL_VAR (textord_new_initial_xheight, TRUE, +"Use test xheight mechanism"); +EXTERN BOOL_VAR (textord_exit_after, FALSE, "Exit after completing textord"); +EXTERN INT_VAR (textord_max_noise_size, 7, "Pixel size of noise"); +EXTERN double_VAR (textord_blob_size_bigile, 95, +"Percentile for large blobs"); +EXTERN double_VAR (textord_noise_area_ratio, 0.7, +"Fraction of bounding box for noise"); +EXTERN double_VAR (textord_blob_size_smallile, 20, +"Percentile for small blobs"); +EXTERN double_VAR (textord_initialx_ile, 0.75, +"Ile of sizes for xheight guess"); +EXTERN double_VAR (textord_initialasc_ile, 0.90, +"Ile of sizes for xheight guess"); +EXTERN INT_VAR (textord_noise_sizefraction, 10, +"Fraction of size for maxima"); +EXTERN double_VAR (textord_noise_sizelimit, 0.5, +"Fraction of x for big t count"); +EXTERN INT_VAR (textord_noise_translimit, 16, "Transitions for normal blob"); +EXTERN double_VAR (textord_noise_normratio, 2.0, +"Dot to norm ratio for deletion"); +EXTERN BOOL_VAR (textord_noise_rejwords, TRUE, "Reject noise-like words"); +EXTERN BOOL_VAR (textord_noise_rejrows, TRUE, "Reject noise-like rows"); +EXTERN double_VAR (textord_noise_syfract, 0.2, +"xh fract error for norm blobs"); +EXTERN double_VAR (textord_noise_sxfract, 0.4, +"xh fract width error for norm blobs"); +EXTERN INT_VAR (textord_noise_sncount, 1, "super norm blobs to save row"); +EXTERN double_VAR (textord_noise_rowratio, 6.0, +"Dot to norm ratio for deletion"); + +EXTERN BOOL_VAR (textord_noise_debug, FALSE, "Debug row garbage detector"); +EXTERN double_VAR (textord_blshift_maxshift, 0.00, "Max baseline shift"); +EXTERN double_VAR (textord_blshift_xfraction, 9.99, +"Min size of baseline shift"); +EXTERN STRING_EVAR (tessedit_image_ext, ".tif", "Externsion for image file"); + +#ifndef EMBEDDED +EXTERN clock_t previous_cpu; +#endif + +extern BOOL_VAR_H (polygon_tess_approximation, TRUE, +"Do tess poly instead of grey scale"); + +#define MAX_NEAREST_DIST 600 //for block skew stats +#define MAX_BLOB_TRANSITIONS100 //for nois stats + +extern IMAGE page_image; //must be defined somewhere +extern BOOL_VAR_H (interactive_mode, TRUE, "Run interactively?"); +extern /*"C" */ ETEXT_DESC *global_monitor; //progress monitor + +/********************************************************************** + * read_and_textord + * + * Read a file of blocks n blobs and textord them. + **********************************************************************/ + +void read_and_textord( //read .pb file + const char *filename, //.pb file + BLOCK_LIST *blocks) { + int c; //input character + FILE *infp; //input file + BLOCK *block; //current block + BOX page_box; //bounding_box + BLOCK_IT block_it = blocks; //iterator + //different orientations + TO_BLOCK_LIST land_blocks, port_blocks; + + infp = fopen (filename, "r"); + if (infp == NULL) + CANTOPENFILE.error ("read_and_textord", EXIT, filename); + + while (((c = fgetc (infp)) != EOF) && (ungetc (c, infp) != EOF)) { + //get one + block = BLOCK::de_serialise (infp); + //add to list + block_it.add_after_then_move (block); + //find page size + page_box += block->bounding_box (); + } + fclose(infp); + + assign_blobs_to_blocks2(blocks, &land_blocks, &port_blocks); + filter_blobs (page_box.topright (), &port_blocks, !textord_test_landscape); + filter_blobs (page_box.topright (), &land_blocks, textord_test_landscape); + textord_page (page_box.topright (), blocks, &land_blocks, &port_blocks); +} + + +/********************************************************************** + * edges_and_textord + * + * Read a file of blocks n blobs and textord them. + **********************************************************************/ + +void edges_and_textord( //read .pb file + const char *filename, //.pb file + BLOCK_LIST *blocks) { + BLOCK *block; //current block + char *lastdot; //of name + STRING name = filename; //truncated name + ICOORD page_tr; + BOX page_box; //bounding_box + PDBLK_CLIST pd_blocks; //copy of list + BLOCK_IT block_it = blocks; //iterator + PDBLK_C_IT pd_it = &pd_blocks; //iterator + //different orientations + TO_BLOCK_LIST land_blocks, port_blocks; + IMAGE thresh_image; //thresholded + + lastdot = strrchr (name.string (), '.'); + if (lastdot != NULL) + *lastdot = '\0'; + if (page_image.get_bpp () == 0) { + name += tessedit_image_ext; + if (page_image.read_header (name.string ())) + CANTOPENFILE.error ("edges_and_textord", EXIT, name.string ()); + if (page_image.read (0)) + READFAILED.error ("edges_and_textord", EXIT, name.string ()); + name = filename; + lastdot = strrchr (name.string (), '.'); + if (lastdot != NULL) + *lastdot = '\0'; + } + page_tr = ICOORD (page_image.get_xsize (), page_image.get_ysize ()); + read_pd_file (name, page_image.get_xsize (), page_image.get_ysize (), + blocks); + block_it.set_to_list (blocks); + if (global_monitor != NULL) + global_monitor->ocr_alive = TRUE; + + if (page_image.get_bpp () > 1) { + set_global_loc_code(LOC_ADAPTIVE); + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + pd_it.add_after_then_move (block); + } + // adaptive_threshold(&page_image,&pd_blocks,&thresh_image); + set_global_loc_code(LOC_EDGE_PROG); +#ifndef EMBEDDED + previous_cpu = clock (); +#endif + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + if (!polygon_tess_approximation) + invert_image(&page_image); +#ifndef GRAPHICS_DISABLED + extract_edges(NO_WINDOW, &page_image, &thresh_image, page_tr, block); +#else + extract_edges(&page_image, &thresh_image, page_tr, block); +#endif + page_box += block->bounding_box (); + } + page_image = thresh_image; //everyone else gets it + } + else { + set_global_loc_code(LOC_EDGE_PROG); + if (!page_image.white_high ()) + invert_image(&page_image); + +#ifndef EMBEDDED + previous_cpu = clock (); +#endif + + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); +#ifndef GRAPHICS_DISABLED + extract_edges(NO_WINDOW, &page_image, &page_image, page_tr, block); +#else + extract_edges(&page_image, &page_image, page_tr, block); +#endif + page_box += block->bounding_box (); + } + } + if (global_monitor != NULL) { + global_monitor->ocr_alive = TRUE; + global_monitor->progress = 10; + } + + assign_blobs_to_blocks2(blocks, &land_blocks, &port_blocks); + if (global_monitor != NULL) + global_monitor->ocr_alive = TRUE; + filter_blobs (page_box.topright (), &land_blocks, textord_test_landscape); +#ifndef EMBEDDED + previous_cpu = clock (); +#endif + filter_blobs (page_box.topright (), &port_blocks, !textord_test_landscape); + if (global_monitor != NULL) + global_monitor->ocr_alive = TRUE; + textord_page (page_box.topright (), blocks, &land_blocks, &port_blocks); +} + +/********************************************************************** + * assign_blobs_to_blocks2 + * + * Make a list of TO_BLOCKs for portrait and landscape orientation. + **********************************************************************/ + +void assign_blobs_to_blocks2( //split into groups + BLOCK_LIST *blocks, //blocks to process + TO_BLOCK_LIST *land_blocks, //rotated for landscape + TO_BLOCK_LIST *port_blocks //output list + ) { + BLOCK *block; //current block + BLOBNBOX *newblob; //created blob + C_BLOB *blob; //current blob + BLOCK_IT block_it = blocks; + C_BLOB_IT blob_it; //iterator + BLOBNBOX_IT port_box_it; //iterator + //destination iterator + TO_BLOCK_IT port_block_it = port_blocks; + TO_BLOCK *port_block; //created block + + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + blob_it.set_to_list (block->blob_list ()); + //make one + port_block = new TO_BLOCK (block); + //make one + port_box_it.set_to_list (&port_block->blobs); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.extract (); + //convert blob + newblob = new BLOBNBOX (blob); + //add to list + port_box_it.add_after_then_move (newblob); + //convert blob + } + port_block_it.add_after_then_move (port_block); + } +} + + +/********************************************************************** + * filter_blobs + * + * Sort the blobs into sizes in all the blocks for later work. + **********************************************************************/ + +void filter_blobs( //split into groups + ICOORD page_tr, //top right + TO_BLOCK_LIST *blocks, //output list + BOOL8 testing_on //for plotting + ) { + TO_BLOCK_IT block_it = blocks; //destination iterator + TO_BLOCK *block; //created block + + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + block->line_size = filter_noise_blobs (&block->blobs, + &block->noise_blobs, + &block->small_blobs, + &block->large_blobs); + block->line_spacing = + block->line_size * (textord_merge_desc + textord_merge_x + + textord_merge_asc + + textord_merge_asc) / textord_merge_x; + block->line_size *= textord_min_linesize; + block->max_blob_size = block->line_size * textord_excess_blobsize; +#ifndef GRAPHICS_DISABLED + if (textord_show_blobs && testing_on) { + if (to_win == NO_WINDOW) + create_to_win(page_tr); + plot_blob_list (to_win, &block->noise_blobs, CORAL, BLUE); + plot_blob_list (to_win, &block->small_blobs, GOLDENROD, YELLOW); + plot_blob_list (to_win, &block->large_blobs, DARK_GREEN, YELLOW); + plot_blob_list (to_win, &block->blobs, WHITE, BROWN); + } + if (textord_show_boxes && testing_on) { + if (to_win == NO_WINDOW) + create_to_win(page_tr); + plot_box_list (to_win, &block->noise_blobs, WHITE); + plot_box_list (to_win, &block->small_blobs, WHITE); + plot_box_list (to_win, &block->large_blobs, WHITE); + plot_box_list (to_win, &block->blobs, WHITE); + } +#endif + } +} + + +/********************************************************************** + * filter_noise_blobs + * + * Move small blobs to a separate list. + **********************************************************************/ + +float filter_noise_blobs( //separate noise + BLOBNBOX_LIST *src_list, //origonal list + BLOBNBOX_LIST *noise_list, //noise list + BLOBNBOX_LIST *small_list, //small blobs + BLOBNBOX_LIST *large_list //large blobs + ) { + INT16 height; //height of blob + INT16 width; //of blob + BLOBNBOX_IT src_it = src_list; //iterators + BLOBNBOX_IT noise_it = noise_list; + BLOBNBOX_IT small_it = small_list; + BLOBNBOX_IT large_it = large_list; + STATS size_stats (0, MAX_NEAREST_DIST); + //blob heights + if (textord_new_initial_xheight) + return filter_noise_blobs2 (src_list, noise_list, small_list, large_list); + float min_y; //size limits + float max_y; + float max_x; + + for (src_it.mark_cycle_pt (); !src_it.cycled_list (); src_it.forward ()) { + if (src_it.data ()->bounding_box ().height () < textord_max_noise_size) + noise_it.add_after_then_move (src_it.extract ()); + } + for (src_it.mark_cycle_pt (); !src_it.cycled_list (); src_it.forward ()) { + size_stats.add (src_it.data ()->bounding_box ().height (), 1); + } + min_y = floor (size_stats.ile (textord_blob_size_smallile / 100.0)); + max_y = ceil (size_stats.ile (textord_blob_size_bigile / 100.0)); + max_x = ceil (size_stats.ile (0.5) * textord_width_limit); + for (src_it.mark_cycle_pt (); !src_it.cycled_list (); src_it.forward ()) { + height = src_it.data ()->bounding_box ().height (); + width = src_it.data ()->bounding_box ().width (); + if (height < min_y) + small_it.add_after_then_move (src_it.extract ()); + else if (height > max_y || width > max_x) + large_it.add_after_then_move (src_it.extract ()); + } + return size_stats.ile (textord_initialx_ile); +} + + +/********************************************************************** + * filter_noise_blobs2 + * + * Move small blobs to a separate list. + **********************************************************************/ + +float filter_noise_blobs2( //separate noise + BLOBNBOX_LIST *src_list, //origonal list + BLOBNBOX_LIST *noise_list, //noise list + BLOBNBOX_LIST *small_list, //small blobs + BLOBNBOX_LIST *large_list //large blobs + ) { + INT16 height; //height of blob + INT16 width; //of blob + BLOBNBOX *blob; //current blob + float initial_x; //first guess + BLOBNBOX_IT src_it = src_list; //iterators + BLOBNBOX_IT noise_it = noise_list; + BLOBNBOX_IT small_it = small_list; + BLOBNBOX_IT large_it = large_list; + STATS size_stats (0, MAX_NEAREST_DIST); + //blob heights + float min_y; //size limits + float max_y; + float max_x; + float max_height; //of good blobs + + for (src_it.mark_cycle_pt (); !src_it.cycled_list (); src_it.forward ()) { + blob = src_it.data (); + if (blob->bounding_box ().height () < textord_max_noise_size) + noise_it.add_after_then_move (src_it.extract ()); + else if (blob->enclosed_area () >= blob->bounding_box ().height () + * blob->bounding_box ().width () * textord_noise_area_ratio) + small_it.add_after_then_move (src_it.extract ()); + } + for (src_it.mark_cycle_pt (); !src_it.cycled_list (); src_it.forward ()) { + size_stats.add (src_it.data ()->bounding_box ().height (), 1); + } + initial_x = size_stats.ile (textord_initialx_ile); + max_y = + ceil (initial_x * + (textord_merge_desc + textord_merge_x + + 2 * textord_merge_asc) / textord_merge_x); + min_y = floor (initial_x / 2); + max_x = ceil (initial_x * textord_width_limit); + small_it.move_to_first (); + for (small_it.mark_cycle_pt (); !small_it.cycled_list (); + small_it.forward ()) { + height = small_it.data ()->bounding_box ().height (); + if (height >= min_y) + large_it.add_after_then_move (small_it.extract ()); + } + size_stats.clear (); + for (src_it.mark_cycle_pt (); !src_it.cycled_list (); src_it.forward ()) { + height = src_it.data ()->bounding_box ().height (); + width = src_it.data ()->bounding_box ().width (); + if (height < min_y) + small_it.add_after_then_move (src_it.extract ()); + else if (height > max_y || width > max_x) + large_it.add_after_then_move (src_it.extract ()); + else + size_stats.add (height, 1); + } + max_height = size_stats.ile (textord_initialasc_ile); + // printf("max_y=%g, min_y=%g, initial_x=%g, max_height=%g,", + // max_y,min_y,initial_x,max_height); + max_height *= textord_merge_x / (textord_merge_x + textord_merge_asc); + if (max_height > initial_x) + initial_x = max_height; + // printf(" ret=%g\n",initial_x); + return initial_x; +} + + +/********************************************************************** + * textord_page + * + * Textord the list of blobs and return a list of proper blocks. + **********************************************************************/ + +void textord_page( //make rows & words + ICOORD page_tr, //top right + BLOCK_LIST *blocks, //block list + TO_BLOCK_LIST *land_blocks, //rotated for landscape + TO_BLOCK_LIST *port_blocks //output list + ) { + float gradient; //global skew + + set_global_loc_code(LOC_TEXT_ORD_ROWS); + gradient = make_rows (page_tr, blocks, land_blocks, port_blocks); + if (global_monitor != NULL) { + global_monitor->ocr_alive = TRUE; + global_monitor->progress = 20; + } + set_global_loc_code(LOC_TEXT_ORD_WORDS); + make_words(page_tr, gradient, blocks, land_blocks, port_blocks); + if (global_monitor != NULL) { + global_monitor->ocr_alive = TRUE; + global_monitor->progress = 30; + } + cleanup_blocks(blocks); //remove empties +#ifndef GRAPHICS_DISABLED + close_to_win(); +#endif + if (textord_exit_after && !interactive_mode) + exit (0); +} + + +/********************************************************************** + * cleanup_blocks + * + * Delete empty blocks, rows from the page. + **********************************************************************/ + +void cleanup_blocks( //remove empties + BLOCK_LIST *blocks //list + ) { + BLOCK_IT block_it = blocks; //iterator + ROW_IT row_it; //row iterator + + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + if (textord_noise_rejrows + && !row_it.data ()->word_list ()->empty () + && clean_noise_from_row (row_it.data ()) + || row_it.data ()->word_list ()->empty ()) + delete row_it.extract ();//lose empty row + else { + if (textord_noise_rejwords) + clean_noise_from_words (row_it.data ()); + if (textord_blshift_maxshift >= 0) + tweak_row_baseline (row_it.data ()); + } + } + if (block_it.data ()->row_list ()->empty ()) { + delete block_it.extract ();//lose empty block + } + } +} + + +/********************************************************************** + * clean_noise_from_row + * + * Move blobs of words from rows of garbage into the reject blobs list. + **********************************************************************/ + +BOOL8 clean_noise_from_row( //remove empties + ROW *row //row to clean + ) { + BOOL8 testing_on; + BOX blob_box; //bounding box + C_BLOB *blob; //current blob + C_OUTLINE *outline; //current outline + WERD *word; //current word + INT32 blob_size; //biggest size + INT32 trans_count = 0; //no of transitions + INT32 trans_threshold; //noise tolerance + INT32 dot_count; //small objects + INT32 norm_count; //normal objects + INT32 super_norm_count; //real char-like + //words of row + WERD_IT word_it = row->word_list (); + C_BLOB_IT blob_it; //blob iterator + C_OUTLINE_IT out_it; //outline iterator + + if (textord_test_y > row->base_line (textord_test_x) + && textord_show_blobs + && textord_test_y < row->base_line (textord_test_x) + row->x_height ()) + testing_on = TRUE; + else + testing_on = FALSE; + dot_count = 0; + norm_count = 0; + super_norm_count = 0; + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); //current word + //blobs in word + blob_it.set_to_list (word->cblob_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + if (!word->flag (W_DONT_CHOP)) { + //get outlines + out_it.set_to_list (blob->out_list ()); + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); + out_it.forward ()) { + outline = out_it.data (); + blob_box = outline->bounding_box (); + blob_size = + blob_box.width () > + blob_box.height ()? blob_box.width () : blob_box. + height(); + if (blob_size < textord_noise_sizelimit * row->x_height ()) + dot_count++; //count smal outlines + if (!outline->child ()->empty () + && blob_box.height () < + (1 + textord_noise_syfract) * row->x_height () + && blob_box.height () > + (1 - textord_noise_syfract) * row->x_height () + && blob_box.width () < + (1 + textord_noise_sxfract) * row->x_height () + && blob_box.width () > + (1 - textord_noise_sxfract) * row->x_height ()) + super_norm_count++; //count smal outlines + } + } + else + super_norm_count++; + blob_box = blob->bounding_box (); + blob_size = + blob_box.width () > + blob_box.height ()? blob_box.width () : blob_box.height (); + if (blob_size >= textord_noise_sizelimit * row->x_height () + && blob_size < row->x_height () * 2) { + trans_threshold = blob_size / textord_noise_sizefraction; + trans_count = blob->count_transitions (trans_threshold); + if (trans_count < textord_noise_translimit) + norm_count++; + } + else if (blob_box.height () > row->x_height () * 2 + && (!word_it.at_first () || !blob_it.at_first ())) + dot_count += 2; + #ifndef SECURE_NAMES + if (testing_on) { + tprintf + ("Blob at (%d,%d) -> (%d,%d), ols=%d, tc=%d, bldiff=%g\n", + blob_box.left (), blob_box.bottom (), blob_box.right (), + blob_box.top (), blob->out_list ()->length (), trans_count, + blob_box.bottom () - row->base_line (blob_box.left ())); + } + #endif + } + } + #ifndef SECURE_NAMES + if (textord_noise_debug) { + tprintf ("Row ending at (%d,%g):", + blob_box.right (), row->base_line (blob_box.right ())); + tprintf (" R=%g, dc=%d, nc=%d, %s\n", + norm_count > 0 ? (float) dot_count / norm_count : 9999, + dot_count, norm_count, + dot_count > norm_count * textord_noise_normratio + && dot_count > 2 ? "REJECTED" : "ACCEPTED"); + } + #endif + return super_norm_count < textord_noise_sncount + && dot_count > norm_count * textord_noise_rowratio && dot_count > 2; +} + + +/********************************************************************** + * clean_noise_from_words + * + * Move blobs of words from rows of garbage into the reject blobs list. + **********************************************************************/ + +void clean_noise_from_words( //remove empties + ROW *row //row to clean + ) { + BOX blob_box; //bounding box + INT8 *word_dud; //was it chucked + C_BLOB *blob; //current blob + C_OUTLINE *outline; //current outline + WERD *word; //current word + INT32 blob_size; //biggest size + INT32 trans_count; //no of transitions + INT32 trans_threshold; //noise tolerance + INT32 dot_count; //small objects + INT32 norm_count; //normal objects + INT32 dud_words; //number discarded + INT32 ok_words; //number remaining + INT32 word_index; //current word + //words of row + WERD_IT word_it = row->word_list (); + C_BLOB_IT blob_it; //blob iterator + C_OUTLINE_IT out_it; //outline iterator + + ok_words = word_it.length (); + if (ok_words == 0) + return; + word_dud = (INT8 *) alloc_mem (ok_words * sizeof (INT8)); + dud_words = 0; + ok_words = 0; + word_index = 0; + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); //current word + dot_count = 0; + norm_count = 0; + //blobs in word + blob_it.set_to_list (word->cblob_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + if (!word->flag (W_DONT_CHOP)) { + //get outlines + out_it.set_to_list (blob->out_list ()); + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); + out_it.forward ()) { + outline = out_it.data (); + blob_box = outline->bounding_box (); + blob_size = + blob_box.width () > + blob_box.height ()? blob_box.width () : blob_box. + height(); + if (blob_size < textord_noise_sizelimit * row->x_height ()) + dot_count++; //count smal outlines + if (!outline->child ()->empty () + && blob_box.height () < + (1 + textord_noise_syfract) * row->x_height () + && blob_box.height () > + (1 - textord_noise_syfract) * row->x_height () + && blob_box.width () < + (1 + textord_noise_sxfract) * row->x_height () + && blob_box.width () > + (1 - textord_noise_sxfract) * row->x_height ()) + norm_count++; //count smal outlines + } + } + else + norm_count++; + blob_box = blob->bounding_box (); + blob_size = + blob_box.width () > + blob_box.height ()? blob_box.width () : blob_box.height (); + if (blob_size >= textord_noise_sizelimit * row->x_height () + && blob_size < row->x_height () * 2) { + trans_threshold = blob_size / textord_noise_sizefraction; + trans_count = blob->count_transitions (trans_threshold); + if (trans_count < textord_noise_translimit) + norm_count++; + } + else if (blob_box.height () > row->x_height () * 2 + && (!word_it.at_first () || !blob_it.at_first ())) + dot_count += 2; + } + if (dot_count > 2) { + if (dot_count > norm_count * textord_noise_normratio * 2) + word_dud[word_index] = 2; + else if (dot_count > norm_count * textord_noise_normratio) + word_dud[word_index] = 1; + else + word_dud[word_index] = 0; + } + else + word_dud[word_index] = 0; + if (word_dud[word_index] == 2) + dud_words++; + else + ok_words++; + word_index++; + } + + word_index = 0; + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + if (word_dud[word_index] == 2 + || word_dud[word_index] == 1 && dud_words > ok_words) { + word = word_it.data (); //current word + //rejected blobs + blob_it.set_to_list (word->rej_cblob_list ()); + //move from blobs + blob_it.add_list_after (word->cblob_list ()); + } + word_index++; + } + free_mem(word_dud); +} + + +/********************************************************************** + * tweak_row_baseline + * + * Shift baseline to fit the blobs more accurately where they are + * close enough. + **********************************************************************/ + +void tweak_row_baseline( //remove empties + ROW *row //row to clean + ) { + BOX blob_box; //bounding box + C_BLOB *blob; //current blob + WERD *word; //current word + INT32 blob_count; //no of blobs + INT32 src_index; //source segment + INT32 dest_index; //destination segment + INT32 *xstarts; //spline segments + double *coeffs; //spline coeffs + float ydiff; //baseline error + float x_centre; //centre of blob + //words of row + WERD_IT word_it = row->word_list (); + C_BLOB_IT blob_it; //blob iterator + + blob_count = 0; + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); //current word + //get total blobs + blob_count += word->cblob_list ()->length (); + } + if (blob_count == 0) + return; + xstarts = + (INT32 *) alloc_mem ((blob_count + row->baseline.segments + 1) * + sizeof (INT32)); + coeffs = + (double *) alloc_mem ((blob_count + row->baseline.segments) * 3 * + sizeof (double)); + + src_index = 0; + dest_index = 0; + xstarts[0] = row->baseline.xcoords[0]; + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); //current word + //blobs in word + blob_it.set_to_list (word->cblob_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + x_centre = (blob_box.left () + blob_box.right ()) / 2.0; + ydiff = blob_box.bottom () - row->base_line (x_centre); + if (ydiff < 0) + ydiff = -ydiff / row->x_height (); + else + ydiff = ydiff / row->x_height (); + if (ydiff < textord_blshift_maxshift + && blob_box.height () / row->x_height () > + textord_blshift_xfraction) { + if (xstarts[dest_index] >= x_centre) + xstarts[dest_index] = blob_box.left (); + coeffs[dest_index * 3] = 0; + coeffs[dest_index * 3 + 1] = 0; + coeffs[dest_index * 3 + 2] = blob_box.bottom (); + //shift it + dest_index++; + xstarts[dest_index] = blob_box.right () + 1; + } + else { + if (xstarts[dest_index] <= x_centre) { + while (row->baseline.xcoords[src_index + 1] <= x_centre + && src_index < row->baseline.segments - 1) { + if (row->baseline.xcoords[src_index + 1] > + xstarts[dest_index]) { + coeffs[dest_index * 3] = + row->baseline.quadratics[src_index].a; + coeffs[dest_index * 3 + 1] = + row->baseline.quadratics[src_index].b; + coeffs[dest_index * 3 + 2] = + row->baseline.quadratics[src_index].c; + dest_index++; + xstarts[dest_index] = + row->baseline.xcoords[src_index + 1]; + } + src_index++; + } + coeffs[dest_index * 3] = + row->baseline.quadratics[src_index].a; + coeffs[dest_index * 3 + 1] = + row->baseline.quadratics[src_index].b; + coeffs[dest_index * 3 + 2] = + row->baseline.quadratics[src_index].c; + dest_index++; + xstarts[dest_index] = row->baseline.xcoords[src_index + 1]; + } + } + } + } + while (src_index < row->baseline.segments + && row->baseline.xcoords[src_index + 1] <= xstarts[dest_index]) + src_index++; + while (src_index < row->baseline.segments) { + coeffs[dest_index * 3] = row->baseline.quadratics[src_index].a; + coeffs[dest_index * 3 + 1] = row->baseline.quadratics[src_index].b; + coeffs[dest_index * 3 + 2] = row->baseline.quadratics[src_index].c; + dest_index++; + src_index++; + xstarts[dest_index] = row->baseline.xcoords[src_index]; + } + //turn to spline + row->baseline = QSPLINE (dest_index, xstarts, coeffs); + free_mem(xstarts); + free_mem(coeffs); +} + + +/********************************************************************** + * blob_y_order + * + * Sort function to sort blobs in y from page top. + **********************************************************************/ + +INT32 blob_y_order( //sort function + void *item1, //items to compare + void *item2) { + //converted ptr + BLOBNBOX *blob1 = *(BLOBNBOX **) item1; + //converted ptr + BLOBNBOX *blob2 = *(BLOBNBOX **) item2; + + if (blob1->bounding_box ().bottom () > blob2->bounding_box ().bottom ()) + return -1; + else if (blob1->bounding_box ().bottom () < + blob2->bounding_box ().bottom ()) + return 1; + else { + if (blob1->bounding_box ().left () < blob2->bounding_box ().left ()) + return -1; + else if (blob1->bounding_box ().left () > + blob2->bounding_box ().left ()) + return 1; + else + return 0; + } +} diff --git a/textord/tordmain.h b/textord/tordmain.h new file mode 100644 index 0000000000..fd4a1290f8 --- /dev/null +++ b/textord/tordmain.h @@ -0,0 +1,132 @@ +/********************************************************************** + * File: tordmain.h (Formerly textordp.h) + * Description: C++ top level textord code. + * Author: Ray Smith + * Created: Tue Jul 28 17:12:33 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TORDMAIN_H +#define TORDMAIN_H + +#include +#include "varable.h" +#include "ocrblock.h" +#include "tessclas.h" +#include "blobbox.h" +#include "notdll.h" + +extern BOOL_VAR_H (textord_show_blobs, FALSE, "Display unsorted blobs"); +extern BOOL_VAR_H (textord_new_initial_xheight, TRUE, +"Use test xheight mechanism"); +extern BOOL_VAR_H (textord_exit_after, FALSE, +"Exit after completing textord"); +extern INT_VAR_H (textord_max_noise_size, 7, "Pixel size of noise"); +extern double_VAR_H (textord_blob_size_bigile, 95, +"Percentile for large blobs"); +extern double_VAR_H (textord_noise_area_ratio, 0.7, +"Fraction of bounding box for noise"); +extern double_VAR_H (textord_blob_size_smallile, 20, +"Percentile for small blobs"); +extern double_VAR_H (textord_initialx_ile, 0.75, +"Ile of sizes for xheight guess"); +extern double_VAR_H (textord_initialasc_ile, 0.90, +"Ile of sizes for xheight guess"); +extern INT_VAR_H (textord_noise_sizefraction, 10, +"Fraction of size for maxima"); +extern double_VAR_H (textord_noise_sizelimit, 0.5, +"Fraction of x for big t count"); +extern INT_VAR_H (textord_noise_translimit, 16, +"Transitions for normal blob"); +extern double_VAR_H (textord_noise_normratio, 2.0, +"Dot to norm ratio for deletion"); +extern BOOL_VAR_H (textord_noise_rejwords, TRUE, "Reject noise-like words"); +extern BOOL_VAR_H (textord_noise_rejrows, TRUE, "Reject noise-like rows"); +extern double_VAR_H (textord_noise_syfract, 0.2, +"xh fract error for norm blobs"); +extern double_VAR_H (textord_noise_sxfract, 0.4, +"xh fract width error for norm blobs"); +extern INT_VAR_H (textord_noise_sncount, 1, "super norm blobs to save row"); +extern double_VAR_H (textord_noise_rowratio, 6.0, +"Dot to norm ratio for deletion"); +extern BOOL_VAR_H (textord_noise_debug, FALSE, "Debug row garbage detector"); +extern double_VAR_H (textord_blshift_maxshift, 0.00, "Max baseline shift"); +extern double_VAR_H (textord_blshift_xfraction, 9.99, +"Min size of baseline shift"); + //xiaofan +extern STRING_EVAR_H (tessedit_image_ext, ".tif", "Externsion for image file"); +extern clock_t previous_cpu; +void make_blocks_from_blobs( //convert & textord + TBLOB *tessblobs, //tess style input + const char *filename, //blob file + ICOORD page_tr, //top right + BOOL8 do_shift, //shift tess coords + BLOCK_LIST *blocks //block list + ); +void read_and_textord( //read .pb file + const char *filename, //.pb file + BLOCK_LIST *blocks); +void edges_and_textord( //read .pb file + const char *filename, //.pb file + BLOCK_LIST *blocks); +void assign_blobs_to_blocks( //split into groups + PBLOB_LIST *blobs, //blobs to distribute + BLOCK_LIST *blocks, //block list + TO_BLOCK_LIST *land_blocks, //rotated for landscape + TO_BLOCK_LIST *port_blocks //output list + ); +void assign_blobs_to_blocks2( //split into groups + BLOCK_LIST *blocks, //blocks to process + TO_BLOCK_LIST *land_blocks, //rotated for landscape + TO_BLOCK_LIST *port_blocks //output list + ); +void filter_blobs( //split into groups + ICOORD page_tr, //top right + TO_BLOCK_LIST *blocks, //output list + BOOL8 testing_on //for plotting + ); +float filter_noise_blobs( //separate noise + BLOBNBOX_LIST *src_list, //origonal list + BLOBNBOX_LIST *noise_list, //noise list + BLOBNBOX_LIST *small_list, //small blobs + BLOBNBOX_LIST *large_list //large blobs + ); +float filter_noise_blobs2( //separate noise + BLOBNBOX_LIST *src_list, //origonal list + BLOBNBOX_LIST *noise_list, //noise list + BLOBNBOX_LIST *small_list, //small blobs + BLOBNBOX_LIST *large_list //large blobs + ); +void textord_page( //make rows & words + ICOORD page_tr, //top right + BLOCK_LIST *blocks, //block list + TO_BLOCK_LIST *land_blocks, //rotated for landscape + TO_BLOCK_LIST *port_blocks //output list + ); +void cleanup_blocks( //remove empties + BLOCK_LIST *blocks //list + ); +BOOL8 clean_noise_from_row( //remove empties + ROW *row //row to clean + ); +void clean_noise_from_words( //remove empties + ROW *row //row to clean + ); +void tweak_row_baseline( //remove empties + ROW *row //row to clean + ); +INT32 blob_y_order( //sort function + void *item1, //items to compare + void *item2); +#endif diff --git a/textord/tospace.cpp b/textord/tospace.cpp new file mode 100644 index 0000000000..c921c2e85a --- /dev/null +++ b/textord/tospace.cpp @@ -0,0 +1,1941 @@ +#include "mfcpch.h" +#include "tovars.h" +#include "drawtord.h" +#include "tospace.h" +#include "ndminx.h" +#include "statistc.h" + +#define EXTERN +EXTERN BOOL_VAR (tosp_old_to_method, FALSE, "Space stats use prechopping?"); +EXTERN BOOL_VAR (tosp_only_use_prop_rows, TRUE, +"Block stats to use fixed pitch rows?"); +EXTERN BOOL_VAR (tosp_use_pre_chopping, FALSE, +"Space stats use prechopping?"); +EXTERN BOOL_VAR (tosp_old_to_bug_fix, FALSE, "Fix suspected bug in old code"); +EXTERN BOOL_VAR (tosp_block_use_cert_spaces, TRUE, +"Only stat OBVIOUS spaces"); +EXTERN BOOL_VAR (tosp_row_use_cert_spaces, TRUE, "Only stat OBVIOUS spaces"); +EXTERN BOOL_VAR (tosp_narrow_blobs_not_cert, TRUE, +"Only stat OBVIOUS spaces"); +EXTERN BOOL_VAR (tosp_row_use_cert_spaces1, TRUE, "Only stat OBVIOUS spaces"); +EXTERN BOOL_VAR (tosp_recovery_isolated_row_stats, TRUE, +"Use row alone when inadequate cert spaces"); +EXTERN BOOL_VAR (tosp_only_small_gaps_for_kern, FALSE, "Better guess"); +EXTERN BOOL_VAR (tosp_all_flips_fuzzy, FALSE, "Pass ANY flip to context?"); +EXTERN BOOL_VAR (tosp_fuzzy_limit_all, TRUE, +"Dont restrict kn->sp fuzzy limit to tables"); +EXTERN BOOL_VAR (tosp_stats_use_xht_gaps, TRUE, +"Use within xht gap for wd breaks"); +EXTERN BOOL_VAR (tosp_use_xht_gaps, TRUE, "Use within xht gap for wd breaks"); +EXTERN BOOL_VAR (tosp_only_use_xht_gaps, FALSE, +"Only use within xht gap for wd breaks"); +EXTERN BOOL_VAR (tosp_rule_9_test_punct, FALSE, +"Dont chng kn to space next to punct"); +EXTERN BOOL_VAR (tosp_flip_fuzz_kn_to_sp, TRUE, "Default flip"); +EXTERN BOOL_VAR (tosp_flip_fuzz_sp_to_kn, TRUE, "Default flip"); +EXTERN BOOL_VAR (tosp_improve_thresh, FALSE, "Enable improvement heuristic"); +EXTERN INT_VAR (tosp_debug_level, 0, "Debug data"); +EXTERN INT_VAR (tosp_enough_space_samples_for_median, 3, +"or should we use mean"); +EXTERN INT_VAR (tosp_redo_kern_limit, 10, +"No.samples reqd to reestimate for row"); +EXTERN INT_VAR (tosp_few_samples, 40, +"No.gaps reqd with 1 large gap to treat as a table"); +EXTERN INT_VAR (tosp_short_row, 20, +"No.gaps reqd with few cert spaces to use certs"); +EXTERN INT_VAR (tosp_sanity_method, 1, "How to avoid being silly"); +EXTERN double_VAR (tosp_threshold_bias1, 0, +"how far between kern and space?"); +EXTERN double_VAR (tosp_threshold_bias2, 0, +"how far between kern and space?"); +EXTERN double_VAR (tosp_narrow_fraction, 0.3, "Fract of xheight for narrow"); +EXTERN double_VAR (tosp_narrow_aspect_ratio, 0.48, +"narrow if w/h less than this"); +EXTERN double_VAR (tosp_wide_fraction, 0.52, "Fract of xheight for wide"); +EXTERN double_VAR (tosp_wide_aspect_ratio, 0.0, "wide if w/h less than this"); +EXTERN double_VAR (tosp_fuzzy_space_factor, 0.6, +"Fract of xheight for fuzz sp"); +EXTERN double_VAR (tosp_fuzzy_space_factor1, 0.5, +"Fract of xheight for fuzz sp"); +EXTERN double_VAR (tosp_fuzzy_space_factor2, 0.72, +"Fract of xheight for fuzz sp"); +EXTERN double_VAR (tosp_gap_factor, 0.83, "gap ratio to flip sp->kern"); +EXTERN double_VAR (tosp_kern_gap_factor1, 2.0, "gap ratio to flip kern->sp"); +EXTERN double_VAR (tosp_kern_gap_factor2, 1.3, "gap ratio to flip kern->sp"); +EXTERN double_VAR (tosp_kern_gap_factor3, 2.5, "gap ratio to flip kern->sp"); +EXTERN double_VAR (tosp_ignore_big_gaps, -1, "xht multiplier"); +EXTERN double_VAR (tosp_ignore_very_big_gaps, 3.5, "xht multiplier"); +EXTERN double_VAR (tosp_rep_space, 1.6, "rep gap multiplier for space"); +EXTERN double_VAR (tosp_enough_small_gaps, 0.65, +"Fract of kerns reqd for isolated row stats"); +EXTERN double_VAR (tosp_table_kn_sp_ratio, 2.25, +"Min difference of kn & sp in table"); +EXTERN double_VAR (tosp_table_xht_sp_ratio, 0.33, +"Expect spaces bigger than this"); +EXTERN double_VAR (tosp_table_fuzzy_kn_sp_ratio, 3.0, +"Fuzzy if less than this"); +EXTERN double_VAR (tosp_fuzzy_kn_fraction, 0.5, "New fuzzy kn alg"); +EXTERN double_VAR (tosp_fuzzy_sp_fraction, 0.5, "New fuzzy sp alg"); +EXTERN double_VAR (tosp_min_sane_kn_sp, 1.5, +"Dont trust spaces less than this time kn"); +EXTERN double_VAR (tosp_init_guess_kn_mult, 2.2, +"Thresh guess - mult kn by this"); +EXTERN double_VAR (tosp_init_guess_xht_mult, 0.28, +"Thresh guess - mult xht by this"); +EXTERN double_VAR (tosp_max_sane_kn_thresh, 5.0, +"Multiplier on kn to limit thresh"); +EXTERN double_VAR (tosp_flip_caution, 0.0, +"Dont autoflip kn to sp when large separation"); + +EXTERN double_VAR (tosp_large_kerning, 0.19, +"Limit use of xht gap with large kns"); +EXTERN double_VAR (tosp_dont_fool_with_small_kerns, -1, +"Limit use of xht gap with odd small kns"); +EXTERN double_VAR (tosp_near_lh_edge, 0, +"Dont reduce box if the top left is non blank"); +EXTERN double_VAR (tosp_silly_kn_sp_gap, 0.2, +"Dont let sp minus kn get too small"); +EXTERN double_VAR (tosp_pass_wide_fuzz_sp_to_context, 0.75, +"How wide fuzzies need context"); + +#define MAXSPACING 128 /*max expected spacing in pix */ +/********************************************************************** + * to_spacing + * + * Compute fuzzy word spacing thresholds for each row. + * I.e. set : max_nonspace + * space_threshold + * min_space + * kern_size + * space_size for each row. + * ONLY FOR PROPORTIONAL BLOCKS - FIXED PITCH IS ASSUMED ALREADY DONE + **********************************************************************/ + +void to_spacing( //set spacing + ICOORD page_tr, //topright of page + TO_BLOCK_LIST *blocks //blocks on page + ) { + TO_BLOCK_IT block_it; //iterator + TO_BLOCK *block; //current block; + TO_ROW_IT row_it; //row iterator + TO_ROW *row; //current row + int block_index; //block number + int row_index; //row number + INT16 block_space_gap_width; //Estimated width of real spaces for whole block + //Estimate width ofnon space gaps for whole block + INT16 block_non_space_gap_width; + //Old fixed/prop result + BOOL8 old_text_ord_proportional; + GAPMAP *gapmap = NULL; //map of big vert gaps in blk + + block_it.set_to_list (blocks); + block_index = 1; + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + gapmap = new GAPMAP (block); + block_spacing_stats(block, + gapmap, + old_text_ord_proportional, + block_space_gap_width, + block_non_space_gap_width); + row_it.set_to_list (block->get_rows ()); + row_index = 1; + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if ((row->pitch_decision == PITCH_DEF_PROP) || + (row->pitch_decision == PITCH_CORR_PROP)) { + if ((tosp_debug_level > 0) && !old_text_ord_proportional) + tprintf ("Block %d Row %d: Now Proportional\n", + block_index, row_index); + row_spacing_stats(row, + gapmap, + block_index, + row_index, + block_space_gap_width, + block_non_space_gap_width); + } + else { + if ((tosp_debug_level > 0) && old_text_ord_proportional) + tprintf + ("Block %d Row %d: Now Fixed Pitch Decision:%d fp flag:%f\n", + block_index, row_index, row->pitch_decision, + row->fixed_pitch); + } +#ifndef GRAPHICS_DISABLED + if (textord_show_initial_words) + plot_word_decisions (to_win, (INT16) row->fixed_pitch, row); +#endif + row_index++; + } + delete gapmap; + block_index++; + } +} + + +/************************************************************************* + * block_spacing_stats() + *************************************************************************/ + +void block_spacing_stats( //DEBUG USE ONLY + TO_BLOCK *block, + GAPMAP *gapmap, + BOOL8 &old_text_ord_proportional, + INT16 &block_space_gap_width, //resulting estimate + INT16 &block_non_space_gap_width //resulting estimate + ) { + TO_ROW_IT row_it; //row iterator + TO_ROW *row; //current row + BLOBNBOX_IT blob_it; //iterator + + STATS centre_to_centre_stats (0, MAXSPACING); + //DEBUG USE ONLY + STATS all_gap_stats (0, MAXSPACING); + STATS space_gap_stats (0, MAXSPACING); + INT16 minwidth = MAX_INT16; //narrowest blob + BOX blob_box; + BOX prev_blob_box; + INT16 centre_to_centre; + INT16 gap_width; + float real_space_threshold; + float iqr_centre_to_centre; //DEBUG USE ONLY + float iqr_all_gap_stats; //DEBUG USE ONLY + INT32 end_of_row; + INT32 row_length; + + row_it.set_to_list (block->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (!row->blob_list ()->empty () && + (!tosp_only_use_prop_rows || + (row->pitch_decision == PITCH_DEF_PROP) || + (row->pitch_decision == PITCH_CORR_PROP))) { + blob_it.set_to_list (row->blob_list ()); + blob_it.mark_cycle_pt (); + end_of_row = blob_it.data_relative (-1)->bounding_box ().right (); + if (tosp_use_pre_chopping) + blob_box = box_next_pre_chopped (&blob_it); + else if (tosp_stats_use_xht_gaps) + blob_box = reduced_box_next (row, &blob_it); + else + blob_box = box_next (&blob_it); + row_length = end_of_row - blob_box.left (); + if (blob_box.width () < minwidth) + minwidth = blob_box.width (); + prev_blob_box = blob_box; + while (!blob_it.cycled_list ()) { + if (tosp_use_pre_chopping) + blob_box = box_next_pre_chopped (&blob_it); + else if (tosp_stats_use_xht_gaps) + blob_box = reduced_box_next (row, &blob_it); + else + blob_box = box_next (&blob_it); + if (blob_box.width () < minwidth) + minwidth = blob_box.width (); + gap_width = blob_box.left () - prev_blob_box.right (); + if (!ignore_big_gap (row, row_length, gapmap, + prev_blob_box.right (), blob_box.left ())) { + all_gap_stats.add (gap_width, 1); + + centre_to_centre = (blob_box.left () + blob_box.right () - + (prev_blob_box.left () + + prev_blob_box.right ())) / 2; + //DEBUG + centre_to_centre_stats.add (centre_to_centre, 1); + // DEBUG + } + prev_blob_box = blob_box; + } + } + } + + //Inadequate samples + if (all_gap_stats.get_total () <= 1) { + block_non_space_gap_width = minwidth; + block_space_gap_width = -1; //No est. space width + //DEBUG + old_text_ord_proportional = TRUE; + } + else { + /* For debug only ..... */ + iqr_centre_to_centre = centre_to_centre_stats.ile (0.75) - + centre_to_centre_stats.ile (0.25); + iqr_all_gap_stats = all_gap_stats.ile (0.75) - all_gap_stats.ile (0.25); + old_text_ord_proportional = + iqr_centre_to_centre * 2 > iqr_all_gap_stats; + /* .......For debug only */ + + /* + The median of the gaps is used as an estimate of the NON-SPACE gap width. + This RELIES on the assumption that there are more gaps WITHIN words than + BETWEEN words in a block + + Now try to estimate the width of a real space for all real spaces in the + block. Do this by using a crude threshold to ignore "narrow" gaps, then + find the median of the "wide" gaps and use this. + */ + block_non_space_gap_width = (INT16) floor (all_gap_stats.median ()); + // median gap + + row_it.set_to_list (block->get_rows ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (!row->blob_list ()->empty () && + (!tosp_only_use_prop_rows || + (row->pitch_decision == PITCH_DEF_PROP) || + (row->pitch_decision == PITCH_CORR_PROP))) { + real_space_threshold = + MAX (tosp_init_guess_kn_mult * block_non_space_gap_width, + tosp_init_guess_xht_mult * row->xheight); + blob_it.set_to_list (row->blob_list ()); + blob_it.mark_cycle_pt (); + end_of_row = + blob_it.data_relative (-1)->bounding_box ().right (); + if (tosp_use_pre_chopping) + blob_box = box_next_pre_chopped (&blob_it); + else if (tosp_stats_use_xht_gaps) + blob_box = reduced_box_next (row, &blob_it); + else + blob_box = box_next (&blob_it); + row_length = blob_box.left () - end_of_row; + prev_blob_box = blob_box; + while (!blob_it.cycled_list ()) { + if (tosp_use_pre_chopping) + blob_box = box_next_pre_chopped (&blob_it); + else if (tosp_stats_use_xht_gaps) + blob_box = reduced_box_next (row, &blob_it); + else + blob_box = box_next (&blob_it); + gap_width = blob_box.left () - prev_blob_box.right (); + if ((gap_width > real_space_threshold) && + !ignore_big_gap (row, row_length, gapmap, + prev_blob_box.right (), + blob_box.left ())) { + /* + If tosp_use_cert_spaces is enabled, the estimate of the space gap is + restricted to obvious spaces - those wider than half the xht or those + with wide blobs on both sides - i.e not things that are suspect 1's or + punctiation that is sometimes widely spaced. + */ + if (!tosp_block_use_cert_spaces || + (gap_width > + tosp_fuzzy_space_factor2 * row->xheight) + || + ((gap_width > + tosp_fuzzy_space_factor1 * row->xheight) + && (!tosp_narrow_blobs_not_cert + || (!narrow_blob (row, prev_blob_box) + && !narrow_blob (row, blob_box)))) + || (wide_blob (row, prev_blob_box) + && wide_blob (row, blob_box))) + space_gap_stats.add (gap_width, 1); + } + prev_blob_box = blob_box; + } + } + } + //Inadequate samples + if (space_gap_stats.get_total () <= 2) + block_space_gap_width = -1;//No est. space width + else + block_space_gap_width = + MAX ((INT16) floor (space_gap_stats.median ()), + 3 * block_non_space_gap_width); + } +} + + +/************************************************************************* + * row_spacing_stats() + * Set values for min_space, max_non_space based on row stats only + * If failure - return 0 values. + *************************************************************************/ + +void row_spacing_stats( //estimate for block + TO_ROW *row, + GAPMAP *gapmap, + INT16 block_idx, + INT16 row_idx, + INT16 block_space_gap_width, + INT16 block_non_space_gap_width //estimate for block + ) { + //iterator + BLOBNBOX_IT blob_it = row->blob_list (); + STATS all_gap_stats (0, MAXSPACING); + STATS cert_space_gap_stats (0, MAXSPACING); + STATS all_space_gap_stats (0, MAXSPACING); + STATS small_gap_stats (0, MAXSPACING); + BOX blob_box; + BOX prev_blob_box; + INT16 gap_width; + INT16 real_space_threshold = 0; + INT16 max = 0; + INT16 index; + INT16 large_gap_count = 0; + BOOL8 suspected_table; + INT32 max_max_nonspace; //upper bound + BOOL8 good_block_space_estimate = block_space_gap_width > 0; + INT32 end_of_row; + INT32 row_length = 0; + float sane_space; + INT32 sane_threshold; + + /* Collect first pass stats for row */ + + if (!good_block_space_estimate) + block_space_gap_width = INT16 (floor (row->xheight / 2)); + if (!row->blob_list ()->empty ()) { + if (tosp_threshold_bias1 > 0) + real_space_threshold = + block_non_space_gap_width + + INT16 (floor (0.5 + + tosp_threshold_bias1 * (block_space_gap_width - + block_non_space_gap_width))); + else + real_space_threshold = //Old TO method + (block_space_gap_width + block_non_space_gap_width) / 2; + blob_it.set_to_list (row->blob_list ()); + blob_it.mark_cycle_pt (); + end_of_row = blob_it.data_relative (-1)->bounding_box ().right (); + if (tosp_use_pre_chopping) + blob_box = box_next_pre_chopped (&blob_it); + else if (tosp_stats_use_xht_gaps) + blob_box = reduced_box_next (row, &blob_it); + else + blob_box = box_next (&blob_it); + row_length = end_of_row - blob_box.left (); + prev_blob_box = blob_box; + while (!blob_it.cycled_list ()) { + if (tosp_use_pre_chopping) + blob_box = box_next_pre_chopped (&blob_it); + else if (tosp_stats_use_xht_gaps) + blob_box = reduced_box_next (row, &blob_it); + else + blob_box = box_next (&blob_it); + gap_width = blob_box.left () - prev_blob_box.right (); + if (ignore_big_gap (row, row_length, gapmap, + prev_blob_box.right (), blob_box.left ())) + large_gap_count++; + else { + if (gap_width >= real_space_threshold) { + if (!tosp_row_use_cert_spaces || + (gap_width > tosp_fuzzy_space_factor2 * row->xheight) || + ((gap_width > tosp_fuzzy_space_factor1 * row->xheight) + && (!tosp_narrow_blobs_not_cert + || (!narrow_blob (row, prev_blob_box) + && !narrow_blob (row, blob_box)))) + || (wide_blob (row, prev_blob_box) + && wide_blob (row, blob_box))) + cert_space_gap_stats.add (gap_width, 1); + all_space_gap_stats.add (gap_width, 1); + } + else + small_gap_stats.add (gap_width, 1); + all_gap_stats.add (gap_width, 1); + } + prev_blob_box = blob_box; + } + } + suspected_table = (large_gap_count > 1) || + ((large_gap_count > 0) && + (all_gap_stats.get_total () <= tosp_few_samples)); + + /* Now determine row kern size, space size and threshold */ + + if ((cert_space_gap_stats.get_total () >= + tosp_enough_space_samples_for_median) || + ((suspected_table || + all_gap_stats.get_total () <= tosp_short_row) && + cert_space_gap_stats.get_total () > 0)) + old_to_method(row, + &all_gap_stats, + &cert_space_gap_stats, + &small_gap_stats, + block_space_gap_width, + block_non_space_gap_width); + else { + if (!tosp_recovery_isolated_row_stats || + !isolated_row_stats (row, gapmap, &all_gap_stats, suspected_table, + block_idx, row_idx)) { + if (tosp_row_use_cert_spaces && (tosp_debug_level > 5)) + tprintf ("B:%d R:%d -- Inadequate certain spaces.\n", + block_idx, row_idx); + if (tosp_row_use_cert_spaces1 && good_block_space_estimate) { + //Use block default + row->space_size = block_space_gap_width; + if (all_gap_stats.get_total () > tosp_redo_kern_limit) + row->kern_size = all_gap_stats.median (); + else + row->kern_size = block_non_space_gap_width; + row->space_threshold = + INT32 (floor ((row->space_size + row->kern_size) / 2)); + } + else + old_to_method(row, + &all_gap_stats, + &all_space_gap_stats, + &small_gap_stats, + block_space_gap_width, + block_non_space_gap_width); + } + } + + if (tosp_improve_thresh && !suspected_table) + improve_row_threshold(row, &all_gap_stats); + + /* Now lets try to be careful not to do anything silly with tables when we + are ignoring big gaps*/ + if (tosp_sanity_method == 0) { + if (suspected_table && + (row->space_size < tosp_table_kn_sp_ratio * row->kern_size)) { + if (tosp_debug_level > 0) + tprintf ("B:%d R:%d -- DONT BELIEVE SPACE %3.2f %d %3.2f.\n", + block_idx, row_idx, + row->kern_size, row->space_threshold, row->space_size); + row->space_threshold = + (INT32) (tosp_table_kn_sp_ratio * row->kern_size); + row->space_size = MAX (row->space_threshold + 1, row->xheight); + } + } + else if (tosp_sanity_method == 1) { + sane_space = row->space_size; + /* NEVER let space size get too close to kern size */ + if ((row->space_size < tosp_min_sane_kn_sp * MAX (row->kern_size, 2.5)) + || ((row->space_size - row->kern_size) < + (tosp_silly_kn_sp_gap * row->xheight))) { + if (good_block_space_estimate && + (block_space_gap_width >= tosp_min_sane_kn_sp * row->kern_size)) + sane_space = block_space_gap_width; + else + sane_space = + MAX (tosp_min_sane_kn_sp * MAX (row->kern_size, 2.5), + row->xheight / 2); + if (tosp_debug_level > 0) + tprintf + ("B:%d R:%d -- DONT BELIEVE SPACE %3.2f %d %3.2f -> %3.2f.\n", + block_idx, row_idx, row->kern_size, row->space_threshold, + row->space_size, sane_space); + row->space_size = sane_space; + row->space_threshold = + INT32 (floor ((row->space_size + row->kern_size) / 2)); + } + /* NEVER let threshold get VERY far away from kern */ + sane_threshold = INT32 (floor (tosp_max_sane_kn_thresh * + MAX (row->kern_size, 2.5))); + if (row->space_threshold > sane_threshold) { + if (tosp_debug_level > 0) + tprintf ("B:%d R:%d -- DONT BELIEVE THRESH %3.2f %d %3.2f->%d.\n", + block_idx, row_idx, + row->kern_size, + row->space_threshold, row->space_size, sane_threshold); + row->space_threshold = sane_threshold; + if (row->space_size <= sane_threshold) + row->space_size = row->space_threshold + 1.0f; + } + /* Beware of tables - there may be NO spaces */ + if (suspected_table) { + sane_space = MAX (tosp_table_kn_sp_ratio * row->kern_size, + tosp_table_xht_sp_ratio * row->xheight); + sane_threshold = INT32 (floor ((sane_space + row->kern_size) / 2)); + + if ((row->space_size < sane_space) || + (row->space_threshold < sane_threshold)) { + if (tosp_debug_level > 0) + tprintf ("B:%d R:%d -- SUSPECT NO SPACES %3.2f %d %3.2f.\n", + block_idx, row_idx, + row->kern_size, + row->space_threshold, row->space_size); + //the minimum sane value + row->space_threshold = (INT32) sane_space; + row->space_size = MAX (row->space_threshold + 1, row->xheight); + } + } + } + + /* Now lets try to put some error limits on the threshold */ + + if (tosp_old_to_method) { + /* Old textord made a space if gap >= threshold */ + //NO FUZZY SPACES YET + row->max_nonspace = row->space_threshold; + //NO FUZZY SPACES YET + row->min_space = row->space_threshold + 1; + } + else { + /* Any gap greater than 0.6 x-ht is bound to be a space (isn't it:-) */ + row->min_space = + MIN (INT32 (ceil (tosp_fuzzy_space_factor * row->xheight)), + INT32 (row->space_size)); + if (row->min_space <= row->space_threshold) + //Dont be silly + row->min_space = row->space_threshold + 1; + /* + Lets try to guess the max certain kern gap by looking at the cluster of + kerns for the row. The row is proportional so the kerns should cluster + tightly at the bottom of the distribution. We also expect most gaps to be + kerns. Find the maximum of the kern piles between 0 and twice the kern + estimate. Piles before the first one with less than 1/10 the maximum + number of samples can be taken as certain kerns. + + Of course, there are some cases where the kern peak and space peaks merge, + so we will put an UPPER limit on the max certain kern gap of some fraction + below the threshold. + */ + + max_max_nonspace = INT32 ((row->space_threshold + row->kern_size) / 2); + + //default + row->max_nonspace = max_max_nonspace; + for (index = 0; index <= max_max_nonspace; index++) { + if (all_gap_stats.pile_count (index) > max) + max = all_gap_stats.pile_count (index); + if ((index > row->kern_size) && + (all_gap_stats.pile_count (index) < 0.1 * max)) { + row->max_nonspace = index; + break; + } + } + } + + /* Yet another algorithm - simpler this time - just choose a fraction of the + threshold to space range */ + + if ((tosp_fuzzy_sp_fraction > 0) && + (row->space_size > row->space_threshold)) + row->min_space = MAX (row->min_space, + (INT32) ceil (row->space_threshold + + tosp_fuzzy_sp_fraction * + (row->space_size - + row->space_threshold))); + + /* Ensure that ANY space less than some multiplier times the kern size is + fuzzy. In tables there is a risk of erroneously setting a small space size + when there are no real spaces. Sometimes tables have text squashed into + columns so that the kn->sp ratio is small anyway - this means that we cant + use this to force a wider separation - hence we rely on context to join any + dubious breaks. */ + + if ((tosp_table_fuzzy_kn_sp_ratio > 0) && + (suspected_table || tosp_fuzzy_limit_all)) + row->min_space = MAX (row->min_space, + (INT32) ceil (tosp_table_fuzzy_kn_sp_ratio * + row->kern_size)); + + if ((tosp_fuzzy_kn_fraction > 0) && (row->kern_size < row->space_threshold)) + row->max_nonspace = (INT32) floor (0.5 + row->kern_size + + tosp_fuzzy_kn_fraction * + (row->space_threshold - + row->kern_size)); + + if (row->max_nonspace > row->space_threshold) + //Dont be silly + row->max_nonspace = row->space_threshold; + + if (tosp_debug_level > 5) + tprintf + ("B:%d R:%d L:%d-- Kn:%d Sp:%d Thr:%d -- Kn:%3.2f (%d) Thr:%d (%d) Sp:%3.2f\n", + block_idx, row_idx, row_length, block_non_space_gap_width, + block_space_gap_width, real_space_threshold, row->kern_size, + row->max_nonspace, row->space_threshold, row->min_space, + row->space_size); +} + + +void old_to_method( //estimate for block + TO_ROW *row, + STATS *all_gap_stats, + STATS *space_gap_stats, + STATS *small_gap_stats, + INT16 block_space_gap_width, + INT16 block_non_space_gap_width //estimate for block + ) { + /* Old to condition was > 2 */ + if (space_gap_stats->get_total () >= tosp_enough_space_samples_for_median) { + //Adequate samples + /* Set space size to median of spaces BUT limits it if it seems wildly out */ + row->space_size = space_gap_stats->median (); + if (row->space_size > block_space_gap_width * 1.5) { + if (tosp_old_to_bug_fix) + row->space_size = block_space_gap_width * 1.5; + else + //BUG??? should be *1.5 + row->space_size = block_space_gap_width; + } + if (row->space_size < (block_non_space_gap_width * 2) + 1) + row->space_size = (block_non_space_gap_width * 2) + 1; + } + //Only 1 or 2 samples + else if (space_gap_stats->get_total () >= 1) { + //hence mean not median + row->space_size = space_gap_stats->mean (); + if (row->space_size > block_space_gap_width * 1.5) { + if (tosp_old_to_bug_fix) + row->space_size = block_space_gap_width * 1.5; + else + //BUG??? should be *1.5 + row->space_size = block_space_gap_width; + } + if (row->space_size < (block_non_space_gap_width * 3) + 1) + row->space_size = (block_non_space_gap_width * 3) + 1; + } + else + //Use block default + row->space_size = block_space_gap_width; + + if ((tosp_only_small_gaps_for_kern) && + (small_gap_stats->get_total () > tosp_redo_kern_limit)) + row->kern_size = small_gap_stats->median (); + else if (all_gap_stats->get_total () > tosp_redo_kern_limit) + row->kern_size = all_gap_stats->median (); + else + //old TO -SAME FOR ALL ROWS + row->kern_size = block_non_space_gap_width; + + if (tosp_threshold_bias2 > 0) + row->space_threshold = + INT32 (floor (0.5 + row->kern_size + + tosp_threshold_bias2 * (row->space_size - + row->kern_size))); + else + /* + NOTE old text ord uses (space_size + kern_size + 1)/2 as the threshold + and holds this in a float. The use is with a >= test + NEW textord uses an integer threshold and a > test + It comes to the same thing. + (Though there is a difference in that old textor has integer space_size + and kern_size.) + */ + row->space_threshold = + INT32 (floor ((row->space_size + row->kern_size) / 2)); +} + + +/************************************************************************* + * isolated_row_stats() + * Set values for min_space, max_non_space based on row stats only + *************************************************************************/ + +BOOL8 isolated_row_stats(TO_ROW *row, + GAPMAP *gapmap, + STATS *all_gap_stats, + BOOL8 suspected_table, + INT16 block_idx, + INT16 row_idx) { + float kern_estimate; + float crude_threshold_estimate; + INT16 small_gaps_count; + INT16 total; + //iterator + BLOBNBOX_IT blob_it = row->blob_list (); + STATS cert_space_gap_stats (0, MAXSPACING); + STATS all_space_gap_stats (0, MAXSPACING); + STATS small_gap_stats (0, MAXSPACING); + BOX blob_box; + BOX prev_blob_box; + INT16 gap_width; + INT32 end_of_row; + INT32 row_length; + + kern_estimate = all_gap_stats->median (); + crude_threshold_estimate = MAX (tosp_init_guess_kn_mult * kern_estimate, + tosp_init_guess_xht_mult * row->xheight); + small_gaps_count = stats_count_under (all_gap_stats, + (INT16) + ceil (crude_threshold_estimate)); + total = all_gap_stats->get_total (); + + if ((total <= tosp_redo_kern_limit) || + ((small_gaps_count / (float) total) < tosp_enough_small_gaps) || + (total - small_gaps_count < 1)) { + if (tosp_debug_level > 5) + tprintf ("B:%d R:%d -- Cant do isolated row stats.\n", + block_idx, row_idx); + return FALSE; + } + blob_it.set_to_list (row->blob_list ()); + blob_it.mark_cycle_pt (); + end_of_row = blob_it.data_relative (-1)->bounding_box ().right (); + if (tosp_use_pre_chopping) + blob_box = box_next_pre_chopped (&blob_it); + else if (tosp_stats_use_xht_gaps) + blob_box = reduced_box_next (row, &blob_it); + else + blob_box = box_next (&blob_it); + row_length = end_of_row - blob_box.left (); + prev_blob_box = blob_box; + while (!blob_it.cycled_list ()) { + if (tosp_use_pre_chopping) + blob_box = box_next_pre_chopped (&blob_it); + else if (tosp_stats_use_xht_gaps) + blob_box = reduced_box_next (row, &blob_it); + else + blob_box = box_next (&blob_it); + gap_width = blob_box.left () - prev_blob_box.right (); + if (!ignore_big_gap (row, row_length, gapmap, + prev_blob_box.right (), blob_box.left ()) && + (gap_width > crude_threshold_estimate)) { + if ((gap_width > tosp_fuzzy_space_factor2 * row->xheight) || + ((gap_width > tosp_fuzzy_space_factor1 * row->xheight) && + (!tosp_narrow_blobs_not_cert || + (!narrow_blob (row, prev_blob_box) && + !narrow_blob (row, blob_box)))) || + (wide_blob (row, prev_blob_box) && wide_blob (row, blob_box))) + cert_space_gap_stats.add (gap_width, 1); + all_space_gap_stats.add (gap_width, 1); + } + if (gap_width < crude_threshold_estimate) + small_gap_stats.add (gap_width, 1); + + prev_blob_box = blob_box; + } + if (cert_space_gap_stats.get_total () >= + tosp_enough_space_samples_for_median) + //median + row->space_size = cert_space_gap_stats.median (); + else if (suspected_table && (cert_space_gap_stats.get_total () > 0)) + //to avoid spaced + row->space_size = cert_space_gap_stats.mean (); + // 1's in tables + else if (all_space_gap_stats.get_total () >= + tosp_enough_space_samples_for_median) + //median + row->space_size = all_space_gap_stats.median (); + else + row->space_size = all_space_gap_stats.mean (); + + if (tosp_only_small_gaps_for_kern) + row->kern_size = small_gap_stats.median (); + else + row->kern_size = all_gap_stats->median (); + row->space_threshold = + INT32 (floor ((row->space_size + row->kern_size) / 2)); + /* Sanity check */ + if ((row->kern_size >= row->space_threshold) || + (row->space_threshold >= row->space_size) || + (row->space_threshold <= 0)) { + if (tosp_debug_level > 0) + tprintf ("B:%d R:%d -- Isolated row stats SANITY FAILURE: %f %d %f\n", + block_idx, row_idx, + row->kern_size, row->space_threshold, row->space_size); + row->kern_size = 0.0f; + row->space_threshold = 0; + row->space_size = 0.0f; + return FALSE; + } + + if (tosp_debug_level > 5) + tprintf ("B:%d R:%d -- Isolated row stats: %f %d %f\n", + block_idx, row_idx, + row->kern_size, row->space_threshold, row->space_size); + return TRUE; +} + + +INT16 stats_count_under(STATS *stats, INT16 threshold) { + INT16 index; + INT16 total = 0; + + for (index = 0; index < threshold; index++) + total += stats->pile_count (index); + return total; +} + + +/************************************************************************* + * improve_row_threshold() + * Try to recognise a "normal line" - + * > 25 gaps + * && space > 3 * kn && space > 10 + * (I.e. reasonably large space and kn:sp ratio) + * && > 3/4 # gaps < kn + (sp - kn)/3 + * (I.e. most gaps are well away from space estimate) + * && a gap of max( 3, (sp - kn)/3 ) empty histogram positions is found + * somewhere in the histogram between kn and sp + * THEN set the threshold and fuzzy limits to this gap - ie NO fuzzies + * NO!!!!! the bristol line has "11" with a gap of 12 between the 1's!!! + * try moving the default threshold to within this band but leave the + * fuzzy limit calculation as at present. + *************************************************************************/ + +void improve_row_threshold(TO_ROW *row, STATS *all_gap_stats) { + float sp = row->space_size; + float kn = row->kern_size; + INT16 reqd_zero_width = 0; + INT16 zero_width = 0; + INT16 zero_start = 0; + INT16 index = 0; + + if (tosp_debug_level > 10) + tprintf ("Improve row threshold 0"); + if ((all_gap_stats->get_total () <= 25) || + (sp <= 10) || + (sp <= 3 * kn) || + (stats_count_under (all_gap_stats, + (INT16) ceil (kn + (sp - kn) / 3 + 0.5)) < + (0.75 * all_gap_stats->get_total ()))) + return; + if (tosp_debug_level > 10) + tprintf (" 1"); + /* + Look for the first region of all 0's in the histogram which is wider than + max( 3, (sp - kn)/3 ) and starts between kn and sp. If found, and current + threshold is not within it, move the threshold so that is is just inside it. + */ + reqd_zero_width = (INT16) floor ((sp - kn) / 3 + 0.5); + if (reqd_zero_width < 3) + reqd_zero_width = 3; + + for (index = INT16 (ceil (kn)); index < INT16 (floor (sp)); index++) { + if (all_gap_stats->pile_count (index) == 0) { + if (zero_width == 0) + zero_start = index; + zero_width++; + } + else { + if (zero_width >= reqd_zero_width) + break; + else { + zero_width = 0; + } + } + } + index--; + if (tosp_debug_level > 10) + tprintf (" reqd_z_width: %d found %d 0's, starting %d; thresh: %d/n", + reqd_zero_width, zero_width, zero_start, row->space_threshold); + if ((zero_width < reqd_zero_width) || + ((row->space_threshold >= zero_start) && + (row->space_threshold <= index))) + return; + if (tosp_debug_level > 10) + tprintf (" 2"); + if (row->space_threshold < zero_start) { + if (tosp_debug_level > 5) + tprintf + ("Improve row kn:%5.2f sp:%5.2f 0's: %d -> %d thresh:%d -> %d\n", + kn, sp, zero_start, index, row->space_threshold, zero_start); + row->space_threshold = zero_start; + } + if (row->space_threshold > index) { + if (tosp_debug_level > 5) + tprintf + ("Improve row kn:%5.2f sp:%5.2f 0's: %d -> %d thresh:%d -> %d\n", + kn, sp, zero_start, index, row->space_threshold, index); + row->space_threshold = index; + } +} + + +/********************************************************************** + * make_prop_words + * + * Convert a TO_BLOCK to a BLOCK. + **********************************************************************/ + +ROW *make_prop_words( //find lines + TO_ROW *row, //row to make + FCOORD rotation //for drawing + ) { + BOOL8 bol; //start of line + /* prev_ values are for start of word being built. non prev_ values are for + the gap between the word being built and the next one. */ + BOOL8 prev_fuzzy_sp; //probably space + BOOL8 prev_fuzzy_non; //probably not + UINT8 prev_blanks; //in front of word + BOOL8 fuzzy_sp; //probably space + BOOL8 fuzzy_non; //probably not + UINT8 blanks; //in front of word + ROW *real_row; //output row + OUTLINE_IT out_it; //outlines + C_OUTLINE_IT cout_it; + PBLOB_LIST blobs; //blobs in word + C_BLOB_LIST cblobs; + PBLOB_IT blob_it = &blobs; //iterator + C_BLOB_IT cblob_it = &cblobs; + WERD_LIST words; + WERD_IT word_it; //new words + WERD *word; //new word + WERD_IT rep_char_it; //repeated char words + INT32 next_rep_char_word_right = MAX_INT32; + float repetition_spacing; //gap between repetitions + INT32 xstarts[2]; //row ends + double coeffs[3]; //quadratic + INT32 prev_x; //end of prev blob + BLOBNBOX *bblob; //current blob + BOX blob_box; //bounding box + BLOBNBOX_IT box_it; //iterator + BOX prev_blob_box; + BOX next_blob_box; + INT16 prev_gap = MAX_INT16; + INT16 current_gap = MAX_INT16; + INT16 next_gap = MAX_INT16; + INT16 prev_within_xht_gap = MAX_INT16; + INT16 current_within_xht_gap = MAX_INT16; + INT16 next_within_xht_gap = MAX_INT16; + INT16 word_count = 0; + static INT16 row_count = 0; + + row_count++; + rep_char_it.set_to_list (&(row->rep_words)); + if (!rep_char_it.empty ()) { + next_rep_char_word_right = + rep_char_it.data ()->bounding_box ().right (); + } + + prev_x = -MAX_INT16; + blob_it.set_to_list (&blobs); + cblob_it.set_to_list (&cblobs); + box_it.set_to_list (row->blob_list ()); + word_it.set_to_list (&words); + bol = TRUE; + prev_blanks = 0; + prev_fuzzy_sp = FALSE; + prev_fuzzy_non = FALSE; + if (!box_it.empty ()) { + xstarts[0] = box_it.data ()->bounding_box ().left (); + if (xstarts[0] > next_rep_char_word_right) { + /* We need to insert a repeated char word at the start of the row */ + word = rep_char_it.extract (); + word_it.add_after_then_move (word); + /* Set spaces before repeated char word */ + word->set_flag (W_BOL, TRUE); + bol = FALSE; + word->set_blanks (0); + //NO uncertainty + word->set_flag (W_FUZZY_SP, FALSE); + word->set_flag (W_FUZZY_NON, FALSE); + xstarts[0] = word->bounding_box ().left (); + /* Set spaces after repeated char word (and leave current word set) */ + repetition_spacing = find_mean_blob_spacing (word); + current_gap = box_it.data ()->bounding_box ().left () - + next_rep_char_word_right; + current_within_xht_gap = current_gap; + if (current_gap > tosp_rep_space * repetition_spacing) { + prev_blanks = (UINT8) floor (current_gap / row->space_size); + if (prev_blanks < 1) + prev_blanks = 1; + } + else + prev_blanks = 0; + if (tosp_debug_level > 5) + tprintf ("Repch wd at BOL(%d, %d). rep spacing %5.2f; Rgap:%d ", + box_it.data ()->bounding_box ().left (), + box_it.data ()->bounding_box ().bottom (), + repetition_spacing, current_gap); + prev_fuzzy_sp = FALSE; + prev_fuzzy_non = FALSE; + if (rep_char_it.empty ()) { + next_rep_char_word_right = MAX_INT32; + } + else { + rep_char_it.forward (); + next_rep_char_word_right = + rep_char_it.data ()->bounding_box ().right (); + } + } + + peek_at_next_gap(row, + box_it, + next_blob_box, + next_gap, + next_within_xht_gap); + do { + bblob = box_it.data (); + blob_box = bblob->bounding_box (); + if (bblob->joined_to_prev ()) { + if (bblob->blob () != NULL) { + out_it.set_to_list (blob_it.data ()->out_list ()); + out_it.move_to_last (); + out_it.add_list_after (bblob->blob ()->out_list ()); + delete bblob->blob (); + } + else if (bblob->cblob () != NULL) { + cout_it.set_to_list (cblob_it.data ()->out_list ()); + cout_it.move_to_last (); + cout_it.add_list_after (bblob->cblob ()->out_list ()); + delete bblob->cblob (); + } + } + else { + if (bblob->blob () != NULL) + blob_it.add_after_then_move (bblob->blob ()); + else if (bblob->cblob () != NULL) + cblob_it.add_after_then_move (bblob->cblob ()); + prev_x = blob_box.right (); + } + box_it.forward (); //next one + bblob = box_it.data (); + blob_box = bblob->bounding_box (); + + if (!bblob->joined_to_prev () && + (bblob->blob () != NULL || bblob->cblob () != NULL)) { + /* Real Blob - not multiple outlines or pre-chopped */ + prev_gap = current_gap; + prev_within_xht_gap = current_within_xht_gap; + prev_blob_box = next_blob_box; + current_gap = next_gap; + current_within_xht_gap = next_within_xht_gap; + peek_at_next_gap(row, + box_it, + next_blob_box, + next_gap, + next_within_xht_gap); + + if ((blob_box.left () > next_rep_char_word_right) || + (!tosp_only_use_xht_gaps && + make_a_word_break (row, blob_box, prev_gap, prev_blob_box, + current_gap, current_within_xht_gap, + next_blob_box, next_gap, + blanks, fuzzy_sp, fuzzy_non)) || + (tosp_only_use_xht_gaps && + make_a_word_break (row, blob_box, prev_within_xht_gap, + prev_blob_box, + current_gap, current_within_xht_gap, + next_blob_box, next_within_xht_gap, + blanks, fuzzy_sp, fuzzy_non)) || + box_it.at_first ()) { + /* Form a new word out of the blobs collected */ + if (!blob_it.empty ()) { + word = new WERD (&blobs, prev_blanks, NULL); + //make real word + word_count++; + } + else { + word = new WERD (&cblobs, prev_blanks, NULL); + word_count++; + } + word_it.add_after_then_move (word); + if (bol) { + word->set_flag (W_BOL, TRUE); + bol = FALSE; + } + if (prev_fuzzy_sp) + //probably space + word->set_flag (W_FUZZY_SP, TRUE); + else if (prev_fuzzy_non) + word->set_flag (W_FUZZY_NON, TRUE); + //probably not + + if (blob_box.left () > next_rep_char_word_right) { + /* We need to insert a repeated char word */ + word = rep_char_it.extract (); + word_it.add_after_then_move (word); + + /* Set spaces before repeated char word */ + repetition_spacing = find_mean_blob_spacing (word); + current_gap = word->bounding_box ().left () - prev_x; + current_within_xht_gap = current_gap; + if (current_gap > tosp_rep_space * repetition_spacing) { + blanks = + (UINT8) floor (current_gap / row->space_size); + if (blanks < 1) + blanks = 1; + } + else + blanks = 0; + if (tosp_debug_level > 5) + tprintf + ("Repch wd (%d,%d) rep gap %5.2f; Lgap:%d (%d blanks);", + word->bounding_box ().left (), + word->bounding_box ().bottom (), + repetition_spacing, current_gap, blanks); + word->set_blanks (blanks); + //NO uncertainty + word->set_flag (W_FUZZY_SP, FALSE); + word->set_flag (W_FUZZY_NON, FALSE); + + /* Set spaces after repeated char word (and leave current word set) */ + current_gap = + blob_box.left () - next_rep_char_word_right; + if (current_gap > tosp_rep_space * repetition_spacing) { + blanks = (UINT8) (current_gap / row->space_size); + if (blanks < 1) + blanks = 1; + } + else + blanks = 0; + if (tosp_debug_level > 5) + tprintf (" Rgap:%d (%d blanks)\n", + current_gap, blanks); + fuzzy_sp = FALSE; + fuzzy_non = FALSE; + + if (rep_char_it.empty ()) { + next_rep_char_word_right = MAX_INT32; + } + else { + rep_char_it.forward (); + next_rep_char_word_right = + rep_char_it.data ()->bounding_box ().right (); + } + } + + if (box_it.at_first () && rep_char_it.empty ()) { + //at end of line + word->set_flag (W_EOL, TRUE); + xstarts[1] = prev_x; + } + else { + prev_blanks = blanks; + prev_fuzzy_sp = fuzzy_sp; + prev_fuzzy_non = fuzzy_non; + } + } + } + } + while (!box_it.at_first ()); //until back at start + + /* Insert any further repeated char words */ + while (!rep_char_it.empty ()) { + word = rep_char_it.extract (); + word_it.add_after_then_move (word); + + /* Set spaces before repeated char word */ + repetition_spacing = find_mean_blob_spacing (word); + current_gap = word->bounding_box ().left () - prev_x; + if (current_gap > tosp_rep_space * repetition_spacing) { + blanks = (UINT8) floor (current_gap / row->space_size); + if (blanks < 1) + blanks = 1; + } + else + blanks = 0; + if (tosp_debug_level > 5) + tprintf + ("Repch wd at EOL (%d,%d). rep spacing %d; Lgap:%d (%d blanks)\n", + word->bounding_box ().left (), word->bounding_box ().bottom (), + repetition_spacing, current_gap, blanks); + word->set_blanks (blanks); + //NO uncertainty + word->set_flag (W_FUZZY_SP, FALSE); + word->set_flag (W_FUZZY_NON, FALSE); + prev_x = word->bounding_box ().right (); + if (rep_char_it.empty ()) { + //at end of line + word->set_flag (W_EOL, TRUE); + xstarts[1] = prev_x; + } + else { + rep_char_it.forward (); + } + } + coeffs[0] = 0; + coeffs[1] = row->line_m (); + coeffs[2] = row->line_c (); + real_row = new ROW (row, + (INT16) row->kern_size, (INT16) row->space_size); + word_it.set_to_list (real_row->word_list ()); + //put words in row + word_it.add_list_after (&words); + real_row->recalc_bounding_box (); + if (tosp_debug_level > 9) { + tprintf ("Row %d Made %d words in row ((%d,%d)(%d,%d))\n", + row_count, + word_count, + real_row->bounding_box ().left (), + real_row->bounding_box ().bottom (), + real_row->bounding_box ().right (), + real_row->bounding_box ().top ()); + } + return real_row; + } + return NULL; +} + + +BOOL8 make_a_word_break( //decide on word break + TO_ROW *row, //row being made + BOX blob_box, //for next_blob //how many blanks? + INT16 prev_gap, + BOX prev_blob_box, + INT16 real_current_gap, + INT16 within_xht_current_gap, + BOX next_blob_box, + INT16 next_gap, + UINT8 &blanks, + BOOL8 &fuzzy_sp, + BOOL8 &fuzzy_non) { + static BOOL8 prev_gap_was_a_space; + BOOL8 space; + INT16 current_gap; + float fuzzy_sp_to_kn_limit; + + /* Inhibit using the reduced gap if + The kerning is large - chars are not kerned and reducing "f"s can cause + erroneous blanks + OR The real gap is less than 0 + OR The real gap is less than the kerning estimate + */ + if ((row->kern_size > tosp_large_kerning * row->xheight) || + ((tosp_dont_fool_with_small_kerns >= 0) && + (real_current_gap < tosp_dont_fool_with_small_kerns * row->kern_size))) + //Ignore the difference + within_xht_current_gap = real_current_gap; + + if (tosp_use_xht_gaps && tosp_only_use_xht_gaps) + current_gap = within_xht_current_gap; + else + current_gap = real_current_gap; + + if (tosp_old_to_method) { + //Boring old method + space = current_gap > row->max_nonspace; + if (space && (current_gap < MAX_INT16)) { + if (current_gap < row->min_space) { + if (current_gap > row->space_threshold) { + blanks = 1; + fuzzy_sp = TRUE; + fuzzy_non = FALSE; + } + else { + blanks = 0; + fuzzy_sp = FALSE; + fuzzy_non = TRUE; + } + } + else { + blanks = (UINT8) (current_gap / row->space_size); + if (blanks < 1) + blanks = 1; + fuzzy_sp = FALSE; + fuzzy_non = FALSE; + } + } + return space; + } + else { + /* New exciting heuristic method */ + if (prev_blob_box.null_box ()) + //Beginning of row + prev_gap_was_a_space = TRUE; + + //Default as old TO + space = current_gap > row->space_threshold; + + /* Set defaults for the word break incase we find one. Currently there are + no fuzzy spaces. Depending on the reliability of the different heuristics + we may need to set PARTICULAR spaces to fuzzy or not. The values will ONLY + be used if the function returns TRUE - ie the word is to be broken. + */ + blanks = (UINT8) (current_gap / row->space_size); + if (blanks < 1) + blanks = 1; + fuzzy_sp = FALSE; + fuzzy_non = FALSE; + /* + If xht measure causes gap to flip one of the 3 thresholds act accordingly - + despite any other heuristics - the MINIMUM action is to pass a fuzzy kern to + context. + */ + if (tosp_use_xht_gaps && + (real_current_gap <= row->max_nonspace) && + (within_xht_current_gap > row->max_nonspace)) { + space = TRUE; + fuzzy_non = TRUE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 20, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + else if (tosp_use_xht_gaps && + (real_current_gap <= row->space_threshold) && + (within_xht_current_gap > row->space_threshold)) { + space = TRUE; + if (tosp_flip_fuzz_kn_to_sp) + fuzzy_sp = TRUE; + else + fuzzy_non = TRUE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 21, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + else if (tosp_use_xht_gaps && + (real_current_gap < row->min_space) && + (within_xht_current_gap >= row->min_space)) { + space = TRUE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 22, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + /* Now continue with normal heuristics */ + else if ((current_gap < row->min_space) && + (current_gap > row->space_threshold)) { + /* Heuristics to turn dubious spaces to kerns */ + if (tosp_pass_wide_fuzz_sp_to_context > 0) + fuzzy_sp_to_kn_limit = row->kern_size + + tosp_pass_wide_fuzz_sp_to_context * + (row->space_size - row->kern_size); + else + fuzzy_sp_to_kn_limit = 99999.0f; + + /* If current gap is significantly smaller than the previous space the other + side of a narrow blob then this gap is a kern. */ + if ((prev_blob_box.width () > 0) && + narrow_blob (row, prev_blob_box) && + prev_gap_was_a_space && + (current_gap <= tosp_gap_factor * prev_gap)) { + if ((tosp_all_flips_fuzzy) || + (current_gap > fuzzy_sp_to_kn_limit)) { + if (tosp_flip_fuzz_sp_to_kn) + fuzzy_non = TRUE; + else + fuzzy_sp = TRUE; + } + else + space = FALSE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 1, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + /* If current gap not much bigger than the previous kern the other side of a + narrow blob then this gap is a kern as well */ + else if ((prev_blob_box.width () > 0) && + narrow_blob (row, prev_blob_box) && + !prev_gap_was_a_space && + (current_gap * tosp_gap_factor <= prev_gap)) { + if ((tosp_all_flips_fuzzy) || + (current_gap > fuzzy_sp_to_kn_limit)) { + if (tosp_flip_fuzz_sp_to_kn) + fuzzy_non = TRUE; + else + fuzzy_sp = TRUE; + } + else + space = FALSE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 2, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + else if ((next_blob_box.width () > 0) && + narrow_blob (row, next_blob_box) && + (next_gap > row->space_threshold) && + (current_gap <= tosp_gap_factor * next_gap)) { + if ((tosp_all_flips_fuzzy) || + (current_gap > fuzzy_sp_to_kn_limit)) { + if (tosp_flip_fuzz_sp_to_kn) + fuzzy_non = TRUE; + else + fuzzy_sp = TRUE; + } + else + space = FALSE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 3, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + else if ((next_blob_box.width () > 0) && + narrow_blob (row, next_blob_box) && + (next_gap <= row->space_threshold) && + (current_gap * tosp_gap_factor <= next_gap)) { + if ((tosp_all_flips_fuzzy) || + (current_gap > fuzzy_sp_to_kn_limit)) { + if (tosp_flip_fuzz_sp_to_kn) + fuzzy_non = TRUE; + else + fuzzy_sp = TRUE; + } + else + space = FALSE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 4, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + else if ((((next_blob_box.width () > 0) && + narrow_blob (row, next_blob_box)) || + ((prev_blob_box.width () > 0) && + narrow_blob (row, prev_blob_box)))) { + fuzzy_sp = TRUE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 6, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + } + else if ((current_gap > row->max_nonspace) && + (current_gap <= row->space_threshold)) { + + /* Heuristics to turn dubious kerns to spaces */ + /* TRIED THIS BUT IT MADE THINGS WORSE + if ( prev_gap == MAX_INT16 ) + prev_gap = 0; //start of row + if ( next_gap == MAX_INT16 ) + next_gap = 0; //end of row + */ + if ((prev_blob_box.width () > 0) && + (next_blob_box.width () > 0) && + (current_gap >= + tosp_kern_gap_factor1 * MAX (prev_gap, next_gap)) && + wide_blob (row, prev_blob_box) && + wide_blob (row, next_blob_box)) { + + space = TRUE; + /* + tosp_flip_caution is an attempt to stop the default changing in cases + where there is a large difference between the kern and space estimates. + See problem in 'chiefs' where "have" gets split in the quotation. + */ + if ((tosp_flip_fuzz_kn_to_sp) && + ((tosp_flip_caution <= 0) || + (tosp_flip_caution * row->kern_size > row->space_size))) + fuzzy_sp = TRUE; + else + fuzzy_non = TRUE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 7, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + else if ((prev_blob_box.width () > 0) && + (next_blob_box.width () > 0) && + (current_gap >= + tosp_kern_gap_factor2 * MAX (prev_gap, next_gap)) && + !(narrow_blob (row, prev_blob_box) || + suspected_punct_blob (row, prev_blob_box)) && + !(narrow_blob (row, next_blob_box) || + suspected_punct_blob (row, next_blob_box))) { + space = TRUE; + fuzzy_non = TRUE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 8, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + else if ((tosp_kern_gap_factor3 > 0) && + (prev_blob_box.width () > 0) && + (next_blob_box.width () > 0) && + (current_gap >= + tosp_kern_gap_factor3 * MAX (prev_gap, next_gap)) && + (!tosp_rule_9_test_punct || + (!suspected_punct_blob (row, prev_blob_box) && + !suspected_punct_blob (row, next_blob_box)))) { + space = TRUE; + fuzzy_non = TRUE; +#ifndef GRAPHICS_DISABLED + mark_gap (blob_box, 9, + prev_gap, prev_blob_box.width (), + current_gap, next_blob_box.width (), next_gap); +#endif + } + } + prev_gap_was_a_space = space && !(fuzzy_non); + return space; + } +} + + +BOOL8 narrow_blob(TO_ROW *row, BOX blob_box) { + BOOL8 result; + + result = ((blob_box.width () <= tosp_narrow_fraction * row->xheight) || + (((float) blob_box.width () / blob_box.height ()) <= + tosp_narrow_aspect_ratio)); + return result; +} + + +BOOL8 wide_blob(TO_ROW *row, BOX blob_box) { + BOOL8 result; + + if (tosp_wide_fraction > 0) { + if (tosp_wide_aspect_ratio > 0) + result = ((blob_box.width () >= tosp_wide_fraction * row->xheight) && + (((float) blob_box.width () / blob_box.height ()) > + tosp_wide_aspect_ratio)); + else + result = (blob_box.width () >= tosp_wide_fraction * row->xheight); + } + else + result = !narrow_blob (row, blob_box); + return result; +} + + +BOOL8 suspected_punct_blob(TO_ROW *row, BOX box) { + BOOL8 result; + float baseline; + float blob_x_centre; + + /* Find baseline of centre of blob */ + + blob_x_centre = (box.right () + box.left ()) / 2.0; + baseline = row->baseline.y (blob_x_centre); + + result = (box.height () <= 0.66 * row->xheight) || + (box.top () < baseline + row->xheight / 2.0) || + (box.bottom () > baseline + row->xheight / 2.0); + return result; +} + + +void peek_at_next_gap( //A COPY FOR PEEKING + TO_ROW *row, + BLOBNBOX_IT box_it, + BOX &next_blob_box, + INT16 &next_gap, + INT16 &next_within_xht_gap) { + BOX next_reduced_blob_box; + BOX bit_beyond; + BLOBNBOX_IT reduced_box_it = box_it; + + next_blob_box = box_next (&box_it); + next_reduced_blob_box = reduced_box_next (row, &reduced_box_it); + if (box_it.at_first ()) { + next_gap = MAX_INT16; + next_within_xht_gap = MAX_INT16; + } + else { + bit_beyond = box_it.data ()->bounding_box (); + next_gap = bit_beyond.left () - next_blob_box.right (); + bit_beyond = reduced_box_next (row, &reduced_box_it); + next_within_xht_gap = + bit_beyond.left () - next_reduced_blob_box.right (); + } +} + + +#ifndef GRAPHICS_DISABLED +void mark_gap( //Debug stuff + BOX blob, //blob following gap + INT16 rule, // heuristic id + INT16 prev_gap, + INT16 prev_blob_width, + INT16 current_gap, + INT16 next_blob_width, + INT16 next_gap) { + COLOUR col; //of ellipse marking flipped gap + + switch (rule) { + case 1: + col = RED; + break; + case 2: + col = CYAN; + break; + case 3: + col = GREEN; + break; + case 4: + col = BLACK; + break; + case 5: + col = MAGENTA; + break; + case 6: + col = BLUE; + break; + + case 7: + col = WHITE; + break; + case 8: + col = YELLOW; + break; + case 9: + col = BLACK; + break; + + case 20: + col = CYAN; + break; + case 21: + col = GREEN; + break; + case 22: + col = MAGENTA; + break; + default: + col = BLACK; + } + if (textord_show_initial_words) { + fill_color_index(to_win, col); + perimeter_color_index(to_win, col); + if (rule < 20) + interior_style(to_win, INT_SOLID, FALSE); + else + interior_style(to_win, INT_HOLLOW, TRUE); + //x radius + ellipse (to_win, current_gap / 2.0f, + blob.height () / 2.0f, //y radius + //x centre + blob.left () - current_gap / 2.0f, + //y centre + blob.bottom () + blob.height () / 2.0f, + 0.0f); + } + if (tosp_debug_level > 0) + tprintf (" (%d,%d) Sp<->Kn Rule %d %d %d %d %d\n", + blob.left () - current_gap / 2, blob.bottom (), rule, + prev_gap, prev_blob_width, current_gap, + next_blob_width, next_gap); +} +#endif + + +float find_mean_blob_spacing(WERD *word) { + PBLOB_IT blob_it; + C_BLOB_IT cblob_it; + BOX blob_box; + INT32 gap_sum = 0; + INT16 gap_count = 0; + INT16 prev_right; + + if (word->flag (W_POLYGON)) { + blob_it.set_to_list (word->blob_list ()); + if (!blob_it.empty ()) { + blob_it.mark_cycle_pt (); + prev_right = blob_it.data ()->bounding_box ().right (); + //first blob + blob_it.forward (); + for (; !blob_it.cycled_list (); blob_it.forward ()) { + blob_box = blob_it.data ()->bounding_box (); + gap_sum += blob_box.left () - prev_right; + gap_count++; + prev_right = blob_box.right (); + } + } + } + else { + cblob_it.set_to_list (word->cblob_list ()); + if (!cblob_it.empty ()) { + cblob_it.mark_cycle_pt (); + prev_right = cblob_it.data ()->bounding_box ().right (); + //first blob + cblob_it.forward (); + for (; !cblob_it.cycled_list (); cblob_it.forward ()) { + blob_box = cblob_it.data ()->bounding_box (); + gap_sum += blob_box.left () - prev_right; + gap_count++; + prev_right = blob_box.right (); + } + } + } + if (gap_count > 0) + return (gap_sum / (float) gap_count); + else + return 0.0f; +} + + +BOOL8 ignore_big_gap(TO_ROW *row, + INT32 row_length, + GAPMAP *gapmap, + INT16 left, + INT16 right) { + INT16 gap = right - left + 1; + + if (tosp_ignore_big_gaps > 999) + return FALSE; //Dont ignore + if (tosp_ignore_big_gaps > 0) + return (gap > tosp_ignore_big_gaps * row->xheight); + if (gap > tosp_ignore_very_big_gaps * row->xheight) + return TRUE; + if (tosp_ignore_big_gaps == 0) { + if ((gap > 2.1 * row->xheight) && (row_length > 20 * row->xheight)) + return TRUE; + if ((gap > 1.75 * row->xheight) && + ((row_length > 35 * row->xheight) || + gapmap->table_gap (left, right))) + return TRUE; + } + else { + /* ONLY time gaps < 3.0 * xht are ignored is when they are part of a table */ + if ((gap > gapmap_big_gaps * row->xheight) && + gapmap->table_gap (left, right)) + return TRUE; + } + return FALSE; +} + + +/********************************************************************** + * reduced_box_next + * + * Compute the bounding box of this blob with merging of x overlaps + * but no pre-chopping. + * Then move the iterator on to the start of the next blob. + * DONT reduce the box for small things - eg punctuation. + **********************************************************************/ + +BOX reduced_box_next( //get bounding box + TO_ROW *row, //current row + BLOBNBOX_IT *it //iterator to blobds + ) { + BLOBNBOX *blob; //current blob + BLOBNBOX *head_blob; //place to store box + BOX full_box; //full blob boundg box + BOX reduced_box; //box of significant part + INT16 left_above_xht; //ABOVE xht left limit + INT16 new_left_above_xht; //ABOVE xht left limit + + blob = it->data (); + if (blob->red_box_set ()) { + reduced_box = blob->reduced_box (); + do { + it->forward (); + blob = it->data (); + } + //until next real blob + while (blob->blob () == NULL && blob->cblob () == NULL || blob->joined_to_prev ()); + return reduced_box; + } + head_blob = blob; + full_box = blob->bounding_box (); + reduced_box = reduced_box_for_blob (blob, row, &left_above_xht); + do { + it->forward (); + blob = it->data (); + if (blob->blob () == NULL && blob->cblob () == NULL) + //was pre-chopped + full_box += blob->bounding_box (); + else if (blob->joined_to_prev ()) { + reduced_box += + reduced_box_for_blob(blob, row, &new_left_above_xht); + left_above_xht = MIN (left_above_xht, new_left_above_xht); + } + } + //until next real blob + while (blob->blob () == NULL && blob->cblob () == NULL || blob->joined_to_prev ()); + + if ((reduced_box.width () > 0) && + ((reduced_box.left () + tosp_near_lh_edge * reduced_box.width ()) + < left_above_xht) && (reduced_box.height () > 0.7 * row->xheight)) { +#ifndef GRAPHICS_DISABLED + if (textord_show_initial_words) + reduced_box.plot (to_win, INT_HOLLOW, TRUE, YELLOW, YELLOW); +#endif + } + else + reduced_box = full_box; + head_blob->set_reduced_box (reduced_box); + return reduced_box; +} + + +/************************************************************************* + * reduced_box_for_blob() + * Find box for blob which is the same height and y position as the whole blob, + * but whose left limit is the left most position of the blob ABOVE the + * baseline and whose right limit is the right most position of the blob BELOW + * the xheight. + * + * + * !!!!!!! WONT WORK WITH LARGE UPPER CASE CHARS - T F V W - look at examples on + * "home". Perhaps we need something which say if the width ABOVE the + * xht alone includes the whole of the reduced width, then use the full + * blob box - Might still fail on italic F + * + * Alternatively we could be a little less severe and only reduce the + * left and right edges by half the difference between the full box and + * the reduced box. + * + * NOTE that we need to rotate all the coordinates as + * find_blob_limits finds the y min and max within a specified x band + *************************************************************************/ + +BOX reduced_box_for_blob(BLOBNBOX *blob, TO_ROW *row, INT16 *left_above_xht) { + float baseline; + float blob_x_centre; + float left_limit; + float right_limit; + float junk; + BOX blob_box; + + /* Find baseline of centre of blob */ + + blob_box = blob->bounding_box (); + blob_x_centre = (blob_box.left () + blob_box.right ()) / 2.0; + baseline = row->baseline.y (blob_x_centre); + + /* + Find LH limit of blob ABOVE the xht. This is so that we can detect certain + caps ht chars which should NOT have their box reduced: T, Y, V, W etc + */ + left_limit = (float) MAX_INT32; + junk = (float) -MAX_INT32; + if (blob->blob () != NULL) + //blob to test + find_blob_limits (blob->blob (), + (float) -MAX_INT16, //rotated lower limit + -(baseline + 1.1 * row->xheight), + //rotated upper limit + FCOORD (0.0, 1.0), //90deg anticlock rot + left_limit, junk); //min y max_y + else + //blob to test + find_cblob_hlimits (blob->cblob (), + //rotated lower limit + (baseline + 1.1 * row->xheight), (float) MAX_INT16, + //rotated upper limit + // FCOORD( 0.0, 1.0 ), //90deg anticlock rot + left_limit, junk); //min y max_y + if (left_limit > junk) + *left_above_xht = MAX_INT16; //No area above xht + else + *left_above_xht = (INT16) floor (left_limit); + /* + Find reduced LH limit of blob - the left extent of the region ABOVE the + baseline. + */ + left_limit = (float) MAX_INT32; + junk = (float) -MAX_INT32; + if (blob->blob () != NULL) + //blob to test + find_blob_limits (blob->blob (), + (float) -MAX_INT16, //rotated lower limit + -baseline, //rotated upper limit + FCOORD (0.0, 1.0), //90deg anticlock rot + left_limit, junk); //min y max_y + else + //blob to test + find_cblob_hlimits (blob->cblob (), + baseline, //rotated upper limit + (float) MAX_INT16, //rotated lower limit + // FCOORD( 0.0, 1.0 ), //90deg anticlock rot + left_limit, junk); //min y max_y + + if (left_limit > junk) + return BOX (); //no area within xht so return empty box + /* + Find reduced RH limit of blob - the right extent of the region BELOW the xht. + */ + junk = (float) MAX_INT32; + right_limit = (float) -MAX_INT32; + if (blob->blob () != NULL) + //blob to test + find_blob_limits (blob->blob (), + -(baseline + row->xheight), + //rotated lower limit + (float) MAX_INT16, //rotated upper limit + FCOORD (0.0, 1.0), //90deg anticlock rot + junk, right_limit); //min y max_y + else + //blob to test + find_cblob_hlimits (blob->cblob (), + (float) -MAX_INT16, //rotated upper limit + (baseline + row->xheight), + //rotated lower limit + // FCOORD( 0.0, 1.0 ), //90deg anticlock rot + junk, right_limit); //min y max_y + if (junk > right_limit) + return BOX (); //no area within xht so return empty box + + return BOX (ICOORD ((INT16) floor (left_limit), blob_box.bottom ()), + ICOORD ((INT16) ceil (right_limit), blob_box.top ())); +} diff --git a/textord/tospace.h b/textord/tospace.h new file mode 100644 index 0000000000..70bd845a8f --- /dev/null +++ b/textord/tospace.h @@ -0,0 +1,193 @@ +/********************************************************************** + * to_spacing + * + * Compute fuzzy word spacing thresholds for each row. + **********************************************************************/ + +#ifndef TOSPACE_H +#define TOSPACE_H + +#include "blobbox.h" +#include "gap_map.h" +#include "statistc.h" +#include "notdll.h" +extern BOOL_VAR_H (tosp_old_to_method, FALSE, "Space stats use prechopping?"); +extern BOOL_VAR_H (tosp_only_use_prop_rows, TRUE, +"Block stats to use fixed pitch rows?"); +extern BOOL_VAR_H (tosp_use_pre_chopping, FALSE, +"Space stats use prechopping?"); +extern BOOL_VAR_H (tosp_old_to_bug_fix, FALSE, +"Fix suspected bug in old code"); +extern BOOL_VAR_H (tosp_block_use_cert_spaces, TRUE, +"Only stat OBVIOUS spaces"); +extern BOOL_VAR_H (tosp_row_use_cert_spaces, TRUE, +"Only stat OBVIOUS spaces"); +extern BOOL_VAR_H (tosp_narrow_blobs_not_cert, TRUE, +"Only stat OBVIOUS spaces"); +extern BOOL_VAR_H (tosp_row_use_cert_spaces1, TRUE, +"Only stat OBVIOUS spaces"); +extern BOOL_VAR_H (tosp_recovery_isolated_row_stats, TRUE, +"Use row alone when inadequate cert spaces"); +extern BOOL_VAR_H (tosp_only_small_gaps_for_kern, FALSE, "Better guess"); +extern BOOL_VAR_H (tosp_all_flips_fuzzy, FALSE, "Pass ANY flip to context?"); +extern BOOL_VAR_H (tosp_fuzzy_limit_all, TRUE, +"Dont restrict kn->sp fuzzy limit to tables"); +extern BOOL_VAR_H (tosp_stats_use_xht_gaps, TRUE, +"Use within xht gap for wd breaks"); +extern BOOL_VAR_H (tosp_use_xht_gaps, TRUE, +"Use within xht gap for wd breaks"); +extern BOOL_VAR_H (tosp_only_use_xht_gaps, FALSE, +"Only use within xht gap for wd breaks"); +extern BOOL_VAR_H (tosp_rule_9_test_punct, FALSE, +"Dont chng kn to space next to punct"); +extern BOOL_VAR_H (tosp_flip_fuzz_kn_to_sp, TRUE, "Default flip"); +extern BOOL_VAR_H (tosp_flip_fuzz_sp_to_kn, TRUE, "Default flip"); +extern BOOL_VAR_H (tosp_improve_thresh, FALSE, +"Enable improvement heuristic"); +extern INT_VAR_H (tosp_debug_level, 0, "Debug data"); +extern INT_VAR_H (tosp_enough_space_samples_for_median, 3, +"or should we use mean"); +extern INT_VAR_H (tosp_redo_kern_limit, 10, +"No.samples reqd to reestimate for row"); +extern INT_VAR_H (tosp_few_samples, 40, +"No.gaps reqd with 1 large gap to treat as a table"); +extern INT_VAR_H (tosp_short_row, 20, +"No.gaps reqd with few cert spaces to use certs"); +extern INT_VAR_H (tosp_sanity_method, 1, "How to avoid being silly"); +extern double_VAR_H (tosp_threshold_bias1, 0, +"how far between kern and space?"); +extern double_VAR_H (tosp_threshold_bias2, 0, +"how far between kern and space?"); +extern double_VAR_H (tosp_narrow_fraction, 0.3, +"Fract of xheight for narrow"); +extern double_VAR_H (tosp_narrow_aspect_ratio, 0.48, +"narrow if w/h less than this"); +extern double_VAR_H (tosp_wide_fraction, 0.52, "Fract of xheight for wide"); +extern double_VAR_H (tosp_wide_aspect_ratio, 0.0, +"wide if w/h less than this"); +extern double_VAR_H (tosp_fuzzy_space_factor, 0.6, +"Fract of xheight for fuzz sp"); +extern double_VAR_H (tosp_fuzzy_space_factor1, 0.5, +"Fract of xheight for fuzz sp"); +extern double_VAR_H (tosp_fuzzy_space_factor2, 0.72, +"Fract of xheight for fuzz sp"); +extern double_VAR_H (tosp_gap_factor, 0.83, "gap ratio to flip sp->kern"); +extern double_VAR_H (tosp_kern_gap_factor1, 2.0, +"gap ratio to flip kern->sp"); +extern double_VAR_H (tosp_kern_gap_factor2, 1.3, +"gap ratio to flip kern->sp"); +extern double_VAR_H (tosp_kern_gap_factor3, 2.5, +"gap ratio to flip kern->sp"); +extern double_VAR_H (tosp_ignore_big_gaps, -1, "xht multiplier"); +extern double_VAR_H (tosp_ignore_very_big_gaps, 3.5, "xht multiplier"); +extern double_VAR_H (tosp_rep_space, 1.6, "rep gap multiplier for space"); +extern double_VAR_H (tosp_enough_small_gaps, 0.65, +"Fract of kerns reqd for isolated row stats"); +extern double_VAR_H (tosp_table_kn_sp_ratio, 2.25, +"Min difference of kn & sp in table"); +extern double_VAR_H (tosp_table_xht_sp_ratio, 0.33, +"Expect spaces bigger than this"); +extern double_VAR_H (tosp_table_fuzzy_kn_sp_ratio, 3.0, +"Fuzzy if less than this"); +extern double_VAR_H (tosp_fuzzy_kn_fraction, 0.5, "New fuzzy kn alg"); +extern double_VAR_H (tosp_fuzzy_sp_fraction, 0.5, "New fuzzy sp alg"); +extern double_VAR_H (tosp_min_sane_kn_sp, 1.5, +"Dont trust spaces less than this time kn"); +extern double_VAR_H (tosp_init_guess_kn_mult, 2.2, +"Thresh guess - mult kn by this"); +extern double_VAR_H (tosp_init_guess_xht_mult, 0.28, +"Thresh guess - mult xht by this"); +extern double_VAR_H (tosp_max_sane_kn_thresh, 5.0, +"Multiplier on kn to limit thresh"); +extern double_VAR_H (tosp_flip_caution, 0.0, +"Dont autoflip kn to sp when large separation"); +extern double_VAR_H (tosp_large_kerning, 0.19, +"Limit use of xht gap with large kns"); +extern double_VAR_H (tosp_dont_fool_with_small_kerns, -1, +"Limit use of xht gap with odd small kns"); +extern double_VAR_H (tosp_near_lh_edge, 0, +"Dont reduce box if the top left is non blank"); +extern double_VAR_H (tosp_silly_kn_sp_gap, 0.2, +"Dont let sp minus kn get too small"); +extern double_VAR_H (tosp_pass_wide_fuzz_sp_to_context, 0.75, +"How wide fuzzies need context"); +void to_spacing( //set spacing + ICOORD page_tr, //topright of page + TO_BLOCK_LIST *blocks //blocks on page + ); + //DEBUG USE ONLY +void block_spacing_stats(TO_BLOCK *block, + GAPMAP *gapmap, + BOOL8 &old_text_ord_proportional, + INT16 &block_space_gap_width, //resulting estimate + INT16 &block_non_space_gap_width //resulting estimate + ); + //estimate for block +void row_spacing_stats(TO_ROW *row, + GAPMAP *gapmap, + INT16 block_idx, + INT16 row_idx, + INT16 block_space_gap_width, + INT16 block_non_space_gap_width //estimate for block + ); + //estimate for block +void old_to_method(TO_ROW *row, + STATS *all_gap_stats, + STATS *space_gap_stats, + STATS *small_gap_stats, + INT16 block_space_gap_width, + INT16 block_non_space_gap_width //estimate for block + ); +BOOL8 isolated_row_stats(TO_ROW *row, + GAPMAP *gapmap, + STATS *all_gap_stats, + BOOL8 suspected_table, + INT16 block_idx, + INT16 row_idx); +INT16 stats_count_under(STATS *stats, INT16 threshold); +void improve_row_threshold(TO_ROW *row, STATS *all_gap_stats); +ROW *make_prop_words( //find lines + TO_ROW *row, //row to make + FCOORD rotation //for drawing + ); +BOOL8 make_a_word_break( //decide on word break + TO_ROW *row, //row being made + BOX blob_box, //for next_blob //how many blanks? + INT16 prev_gap, + BOX prev_blob_box, + INT16 real_current_gap, + INT16 within_xht_current_gap, + BOX next_blob_box, + INT16 next_gap, + UINT8 &blanks, + BOOL8 &fuzzy_sp, + BOOL8 &fuzzy_non); +BOOL8 narrow_blob(TO_ROW *row, BOX blob_box); +BOOL8 wide_blob(TO_ROW *row, BOX blob_box); +BOOL8 suspected_punct_blob(TO_ROW *row, BOX box); + //A COPY FOR PEEKING +void peek_at_next_gap(TO_ROW *row, + BLOBNBOX_IT box_it, + BOX &next_blob_box, + INT16 &next_gap, + INT16 &next_within_xht_gap); +void mark_gap( //Debug stuff + BOX blob, //blob following gap + INT16 rule, // heuristic id + INT16 prev_gap, + INT16 prev_blob_width, + INT16 current_gap, + INT16 next_blob_width, + INT16 next_gap); +float find_mean_blob_spacing(WERD *word); +BOOL8 ignore_big_gap(TO_ROW *row, + INT32 row_length, + GAPMAP *gapmap, + INT16 left, + INT16 right); +BOX reduced_box_next( //get bounding box + TO_ROW *row, //current row + BLOBNBOX_IT *it //iterator to blobds + ); +BOX reduced_box_for_blob(BLOBNBOX *blob, TO_ROW *row, INT16 *left_above_xht); +#endif diff --git a/textord/tovars.cpp b/textord/tovars.cpp new file mode 100644 index 0000000000..fa36c27f47 --- /dev/null +++ b/textord/tovars.cpp @@ -0,0 +1,87 @@ +/********************************************************************** + * File: tovars.cpp (Formerly to_vars.c) + * Description: Variables used by textord. + * Author: Ray Smith + * Created: Tue Aug 24 16:55:02 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "tovars.h" + +#define EXTERN + +EXTERN BOOL_VAR (textord_show_initial_words, FALSE, "Display separate words"); +EXTERN BOOL_VAR (textord_show_new_words, FALSE, "Display separate words"); +EXTERN BOOL_VAR (textord_show_fixed_words, FALSE, +"Display forced fixed pitch words"); +EXTERN BOOL_VAR (textord_blocksall_fixed, FALSE, "Moan about prop blocks"); +EXTERN BOOL_VAR (textord_blocksall_prop, FALSE, +"Moan about fixed pitch blocks"); +EXTERN BOOL_VAR (textord_blocksall_testing, FALSE, "Dump stats when moaning"); +EXTERN BOOL_VAR (textord_test_mode, FALSE, "Do current test"); +EXTERN BOOL_VAR (textord_repeat_extraction, TRUE, "Extract repeated chars"); +EXTERN INT_VAR (textord_dotmatrix_gap, 3, +"Max pixel gap for broken pixed pitch"); +EXTERN INT_VAR (textord_repeat_threshold, 4, +"Min multiple for repeated char"); +EXTERN INT_VAR (textord_debug_block, 0, "Block to do debug on"); +EXTERN INT_VAR (textord_pitch_range, 2, "Max range test on pitch"); +EXTERN double_VAR (textord_repeat_rating, 6, "Min rating for equal blobs"); +EXTERN double_VAR (textord_wordstats_smooth_factor, 0.05, +"Smoothing gap stats"); +EXTERN double_VAR (textord_width_smooth_factor, 0.10, +"Smoothing width stats"); +EXTERN double_VAR (textord_words_width_ile, 0.4, +"Ile of blob widths for space est"); +EXTERN double_VAR (textord_words_maxspace, 4.0, "Multiple of xheight"); +EXTERN double_VAR (textord_words_default_maxspace, 3.5, +"Max believable third space"); +EXTERN double_VAR (textord_words_default_minspace, 0.6, +"Fraction of xheight"); +EXTERN double_VAR (textord_words_min_minspace, 0.3, "Fraction of xheight"); +EXTERN double_VAR (textord_words_default_nonspace, 0.2, +"Fraction of xheight"); +EXTERN double_VAR (textord_words_initial_lower, 0.25, +"Max inital cluster size"); +EXTERN double_VAR (textord_words_initial_upper, 0.15, +"Min initial cluster spacing"); +EXTERN double_VAR (textord_words_minlarge, 0.75, +"Fraction of valid gaps needed"); +EXTERN double_VAR (textord_words_pitchsd_threshold, 0.040, +"Pitch sync threshold"); +EXTERN double_VAR (textord_words_def_fixed, 0.016, +"Threshold for definite fixed"); +EXTERN double_VAR (textord_words_def_prop, 0.090, +"Threshold for definite prop"); +EXTERN INT_VAR (textord_words_veto_power, 5, +"Rows required to outvote a veto"); +EXTERN double_VAR (textord_pitch_rowsimilarity, 0.08, +"Fraction of xheight for sameness"); +EXTERN BOOL_VAR (textord_pitch_scalebigwords, FALSE, +"Scale scores on big words"); +EXTERN double_VAR (words_initial_lower, 0.5, "Max inital cluster size"); +EXTERN double_VAR (words_initial_upper, 0.15, "Min initial cluster spacing"); +EXTERN double_VAR (words_default_prop_nonspace, 0.25, "Fraction of xheight"); +EXTERN double_VAR (words_default_fixed_space, 0.75, "Fraction of xheight"); +EXTERN double_VAR (words_default_fixed_limit, 0.6, "Allowed size variance"); +EXTERN double_VAR (textord_words_definite_spread, 0.30, +"Non-fuzzy spacing region"); +EXTERN double_VAR (textord_spacesize_ratiofp, 2.8, +"Min ratio space/nonspace"); +EXTERN double_VAR (textord_spacesize_ratioprop, 2.0, +"Min ratio space/nonspace"); +EXTERN double_VAR (textord_fpiqr_ratio, 1.5, "Pitch IQR/Gap IQR threshold"); +EXTERN double_VAR (textord_max_pitch_iqr, 0.20, "Xh fraction noise in pitch"); +EXTERN double_VAR (textord_fp_min_width, 0.5, "Min width of decent blobs"); diff --git a/textord/tovars.h b/textord/tovars.h new file mode 100644 index 0000000000..96698246cd --- /dev/null +++ b/textord/tovars.h @@ -0,0 +1,94 @@ +/********************************************************************** + * File: tovars.h (Formerly to_vars.h) + * Description: Variables used by textord. + * Author: Ray Smith + * Created: Tue Aug 24 16:55:02 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TOVARS_H +#define TOVARS_H + +#include "varable.h" +#include "notdll.h" + +extern BOOL_VAR_H (textord_show_initial_words, FALSE, +"Display separate words"); +extern BOOL_VAR_H (textord_show_new_words, FALSE, "Display separate words"); +extern BOOL_VAR_H (textord_show_fixed_words, FALSE, +"Display forced fixed pitch words"); +extern BOOL_VAR_H (textord_blocksall_fixed, FALSE, "Moan about prop blocks"); +extern BOOL_VAR_H (textord_blocksall_prop, FALSE, +"Moan about fixed pitch blocks"); +extern BOOL_VAR_H (textord_blocksall_testing, FALSE, +"Dump stats when moaning"); +extern BOOL_VAR_H (textord_test_mode, FALSE, "Do current test"); +extern BOOL_VAR_H (textord_repeat_extraction, TRUE, "Extract repeated chars"); +extern INT_VAR_H (textord_dotmatrix_gap, 3, +"Max pixel gap for broken pixed pitch"); +extern INT_VAR_H (textord_repeat_threshold, 4, +"Min multiple for repeated char"); +extern INT_VAR_H (textord_debug_block, 0, "Block to do debug on"); +extern INT_VAR_H (textord_pitch_range, 2, "Max range test on pitch"); +extern double_VAR_H (textord_repeat_rating, 6, "Min rating for equal blobs"); +extern double_VAR_H (textord_wordstats_smooth_factor, 0.05, +"Smoothing gap stats"); +extern double_VAR_H (textord_width_smooth_factor, 0.10, +"Smoothing width stats"); +extern double_VAR_H (textord_words_width_ile, 0.4, +"Ile of blob widths for space est"); +extern double_VAR_H (textord_words_maxspace, 4.0, "Multiple of xheight"); +extern double_VAR_H (textord_words_default_maxspace, 3.5, +"Max believable third space"); +extern double_VAR_H (textord_words_default_minspace, 0.6, +"Fraction of xheight"); +extern double_VAR_H (textord_words_min_minspace, 0.3, "Fraction of xheight"); +extern double_VAR_H (textord_words_default_nonspace, 0.2, +"Fraction of xheight"); +extern double_VAR_H (textord_words_initial_lower, 0.25, +"Max inital cluster size"); +extern double_VAR_H (textord_words_initial_upper, 0.15, +"Min initial cluster spacing"); +extern double_VAR_H (textord_words_minlarge, 0.75, +"Fraction of valid gaps needed"); +extern double_VAR_H (textord_words_pitchsd_threshold, 0.025, +"Pitch sync threshold"); +extern double_VAR_H (textord_words_def_fixed, 0.01, +"Threshold for definite fixed"); +extern double_VAR_H (textord_words_def_prop, 0.06, +"Threshold for definite prop"); +extern INT_VAR_H (textord_words_veto_power, 5, +"Rows required to outvote a veto"); +extern double_VAR_H (textord_pitch_rowsimilarity, 0.08, +"Fraction of xheight for sameness"); +extern BOOL_VAR_H (textord_pitch_scalebigwords, FALSE, +"Scale scores on big words"); +extern double_VAR_H (words_initial_lower, 0.5, "Max inital cluster size"); +extern double_VAR_H (words_initial_upper, 0.15, +"Min initial cluster spacing"); +extern double_VAR_H (words_default_prop_nonspace, 0.25, +"Fraction of xheight"); +extern double_VAR_H (words_default_fixed_space, 0.75, "Fraction of xheight"); +extern double_VAR_H (words_default_fixed_limit, 0.6, "Allowed size variance"); +extern double_VAR_H (textord_words_definite_spread, 0.30, +"Non-fuzzy spacing region"); +extern double_VAR_H (textord_spacesize_ratiofp, 2.8, +"Min ratio space/nonspace"); +extern double_VAR_H (textord_spacesize_ratioprop, 2.0, +"Min ratio space/nonspace"); +extern double_VAR_H (textord_fpiqr_ratio, 1.5, "Pitch IQR/Gap IQR threshold"); +extern double_VAR_H (textord_max_pitch_iqr, 0.20, +"Xh fraction noise in pitch"); +extern double_VAR_H (textord_fp_min_width, 0.5, "Min width of decent blobs"); +#endif diff --git a/textord/underlin.cpp b/textord/underlin.cpp new file mode 100644 index 0000000000..ae0f54c6c4 --- /dev/null +++ b/textord/underlin.cpp @@ -0,0 +1,312 @@ +/********************************************************************** + * File: underlin.cpp (Formerly undrline.c) + * Description: Code to chop blobs apart from underlines. + * Author: Ray Smith + * Created: Mon Aug 8 11:14:00 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include "underlin.h" + +#define PROJECTION_MARGIN 10 //arbitrary +#define EXTERN + +EXTERN double_VAR (textord_underline_offset, 0.1, "Fraction of x to ignore"); +EXTERN BOOL_VAR (textord_restore_underlines, TRUE, +"Chop underlines & put back"); + +/********************************************************************** + * restore_underlined_blobs + * + * Find underlined blobs and put them back in the row. + **********************************************************************/ + +void restore_underlined_blobs( //get chop points + TO_BLOCK *block //block to do + ) { + INT16 chop_coord; //chop boundary + BOX blob_box; //of underline + BLOBNBOX *u_line; //underline bit + TO_ROW *row; //best row for blob + ICOORDELT_LIST chop_cells; //blobs to cut out + //real underlines + BLOBNBOX_LIST residual_underlines; + OUTLINE_LIST left_outlines; //in current blob + OUTLINE_LIST right_outlines; //for next blob + C_OUTLINE_LIST left_coutlines; + C_OUTLINE_LIST right_coutlines; + ICOORDELT_IT cell_it = &chop_cells; + //under lines + BLOBNBOX_IT under_it = &block->underlines; + BLOBNBOX_IT ru_it = &residual_underlines; + + if (block->get_rows()->empty()) + return; // Don't crash if there are no rows. + for (under_it.mark_cycle_pt (); !under_it.cycled_list (); + under_it.forward ()) { + u_line = under_it.extract (); + blob_box = u_line->bounding_box (); + row = most_overlapping_row (block->get_rows (), u_line); + find_underlined_blobs (u_line, &row->baseline, row->xheight, + row->xheight * textord_underline_offset, + &chop_cells); + cell_it.set_to_list (&chop_cells); + for (cell_it.mark_cycle_pt (); !cell_it.cycled_list (); + cell_it.forward ()) { + chop_coord = cell_it.data ()->x (); + if (cell_it.data ()->y () - chop_coord > textord_fp_chop_error + 1) { + split_to_blob (u_line, chop_coord, + textord_fp_chop_error + 0.5, + &left_outlines, &left_coutlines, + &right_outlines, &right_coutlines); + if (!left_outlines.empty ()) + ru_it. + add_after_then_move (new + BLOBNBOX (new PBLOB (&left_outlines))); + else if (!left_coutlines.empty ()) + ru_it. + add_after_then_move (new + BLOBNBOX (new + C_BLOB (&left_coutlines))); + //right edge of lbob + chop_coord = cell_it.data ()->y (); + split_to_blob (NULL, chop_coord, + textord_fp_chop_error + 0.5, + &left_outlines, &left_coutlines, + &right_outlines, &right_coutlines); + if (!left_outlines.empty ()) + row->insert_blob (new BLOBNBOX (new PBLOB (&left_outlines))); + else if (!left_coutlines.empty ()) + row-> + insert_blob (new BLOBNBOX (new C_BLOB (&left_coutlines))); + else { + ASSERT_HOST(FALSE); + fprintf (stderr, + "Error:no outlines after chopping from %d to %d from (%d,%d)->(%d,%d)\n", + cell_it.data ()->x (), cell_it.data ()->y (), + blob_box.left (), blob_box.bottom (), + blob_box.right (), blob_box.top ()); + } + u_line = NULL; //no more blobs to add + } + delete cell_it.extract (); + } + if (!right_outlines.empty () || !right_coutlines.empty ()) { + split_to_blob (NULL, blob_box.right (), + textord_fp_chop_error + 0.5, + &left_outlines, &left_coutlines, + &right_outlines, &right_coutlines); + if (!left_outlines.empty ()) + ru_it. + add_after_then_move (new BLOBNBOX (new PBLOB (&left_outlines))); + else if (!left_coutlines.empty ()) + ru_it. + add_after_then_move (new + BLOBNBOX (new C_BLOB (&left_coutlines))); + } + if (u_line != NULL) { + if (u_line->blob() != NULL) + delete u_line->blob(); + if (u_line->cblob() != NULL) + delete u_line->cblob(); + delete u_line; + } + } + if (!ru_it.empty ()) { + ru_it.move_to_first (); + for (ru_it.mark_cycle_pt (); !ru_it.cycled_list (); ru_it.forward ()) { + under_it.add_after_then_move (ru_it.extract ()); + } + } +} + + +/********************************************************************** + * most_overlapping_row + * + * Return the row which most overlaps the blob. + **********************************************************************/ + +TO_ROW *most_overlapping_row( //find best row + TO_ROW_LIST *rows, //list of rows + BLOBNBOX *blob //blob to place + ) { + INT16 x = (blob->bounding_box ().left () + + blob->bounding_box ().right ()) / 2; + TO_ROW_IT row_it = rows; //row iterator + TO_ROW *row; //current row + TO_ROW *best_row; //output row + float overlap; //of blob & row + float bestover; //best overlap + + best_row = NULL; + bestover = (float) -MAX_INT32; + if (row_it.empty ()) + return NULL; + row = row_it.data (); + row_it.mark_cycle_pt (); + while (row->baseline.y (x) + row->descdrop > blob->bounding_box ().top () + && !row_it.cycled_list ()) { + best_row = row; + bestover = + blob->bounding_box ().top () - row->baseline.y (x) + row->descdrop; + row_it.forward (); + row = row_it.data (); + } + while (row->baseline.y (x) + row->xheight + row->ascrise + >= blob->bounding_box ().bottom () && !row_it.cycled_list ()) { + overlap = row->baseline.y (x) + row->xheight + row->ascrise; + if (blob->bounding_box ().top () < overlap) + overlap = blob->bounding_box ().top (); + if (blob->bounding_box ().bottom () > + row->baseline.y (x) + row->descdrop) + overlap -= blob->bounding_box ().bottom (); + else + overlap -= row->baseline.y (x) + row->descdrop; + if (overlap > bestover) { + bestover = overlap; + best_row = row; + } + row_it.forward (); + row = row_it.data (); + } + if (bestover < 0 + && row->baseline.y (x) + row->xheight + row->ascrise + - blob->bounding_box ().bottom () > bestover) + best_row = row; + return best_row; +} + + +/********************************************************************** + * find_underlined_blobs + * + * Find the start and end coords of blobs in the underline. + **********************************************************************/ + +void find_underlined_blobs( //get chop points + BLOBNBOX *u_line, //underlined unit + QSPLINE *baseline, //actual baseline + float xheight, //height of line + float baseline_offset, //amount to shrinke it + ICOORDELT_LIST *chop_cells //places to chop + ) { + INT16 x, y; //sides of blob + ICOORD blob_chop; //sides of blob + BOX blob_box = u_line->bounding_box (); + //cell iterator + ICOORDELT_IT cell_it = chop_cells; + STATS upper_proj (blob_box.left (), blob_box.right () + 1); + STATS middle_proj (blob_box.left (), blob_box.right () + 1); + STATS lower_proj (blob_box.left (), blob_box.right () + 1); + C_OUTLINE_IT out_it; //outlines of blob + + ASSERT_HOST (u_line->cblob () != NULL); + + out_it.set_to_list (u_line->cblob ()->out_list ()); + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + vertical_cunderline_projection (out_it.data (), + baseline, xheight, baseline_offset, + &lower_proj, &middle_proj, &upper_proj); + } + + for (x = blob_box.left (); x < blob_box.right (); x++) { + if (middle_proj.pile_count (x) > 0) { + for (y = x + 1; + y < blob_box.right () && middle_proj.pile_count (y) > 0; y++); + blob_chop = ICOORD (x, y); + cell_it.add_after_then_move (new ICOORDELT (blob_chop)); + x = y; + } + } +} + + +/********************************************************************** + * vertical_cunderline_projection + * + * Compute the vertical projection of a outline from its outlines + * and add to the given STATS. + **********************************************************************/ + +void vertical_cunderline_projection( //project outlines + C_OUTLINE *outline, //outline to project + QSPLINE *baseline, //actual baseline + float xheight, //height of line + float baseline_offset, //amount to shrinke it + STATS *lower_proj, //below baseline + STATS *middle_proj, //centre region + STATS *upper_proj //top region + ) { + ICOORD pos; //current point + ICOORD step; //edge step + INT16 lower_y, upper_y; //region limits + INT32 length; //of outline + INT16 stepindex; //current step + C_OUTLINE_IT out_it = outline->child (); + + pos = outline->start_pos (); + length = outline->pathlength (); + for (stepindex = 0; stepindex < length; stepindex++) { + step = outline->step (stepindex); + if (step.x () > 0) { + lower_y = + (INT16) floor (baseline->y (pos.x ()) + baseline_offset + 0.5); + upper_y = + (INT16) floor (baseline->y (pos.x ()) + baseline_offset + + xheight + 0.5); + if (pos.y () >= lower_y) { + lower_proj->add (pos.x (), -lower_y); + if (pos.y () >= upper_y) { + middle_proj->add (pos.x (), lower_y - upper_y); + upper_proj->add (pos.x (), upper_y - pos.y ()); + } + else + middle_proj->add (pos.x (), lower_y - pos.y ()); + } + else + lower_proj->add (pos.x (), -pos.y ()); + } + else if (step.x () < 0) { + lower_y = + (INT16) floor (baseline->y (pos.x () - 1) + baseline_offset + + 0.5); + upper_y = + (INT16) floor (baseline->y (pos.x () - 1) + baseline_offset + + xheight + 0.5); + if (pos.y () >= lower_y) { + lower_proj->add (pos.x () - 1, lower_y); + if (pos.y () >= upper_y) { + middle_proj->add (pos.x () - 1, upper_y - lower_y); + upper_proj->add (pos.x () - 1, pos.y () - upper_y); + } + else + middle_proj->add (pos.x () - 1, pos.y () - lower_y); + } + else + lower_proj->add (pos.x () - 1, pos.y ()); + } + pos += step; + } + + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + vertical_cunderline_projection (out_it.data (), + baseline, xheight, baseline_offset, + lower_proj, middle_proj, upper_proj); + } +} diff --git a/textord/underlin.h b/textord/underlin.h new file mode 100644 index 0000000000..0b8aa8518c --- /dev/null +++ b/textord/underlin.h @@ -0,0 +1,53 @@ +/********************************************************************** + * File: underlin.h (Formerly undrline.h) + * Description: Code to chop blobs apart from underlines. + * Author: Ray Smith + * Created: Mon Aug 8 11:14:00 BST 1994 + * + * (C) Copyright 1994, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef UNDERLIN_H +#define UNDERLIN_H + +#include "fpchop.h" +#include "notdll.h" + +extern double_VAR_H (textord_underline_offset, 0.1, +"Fraction of x to ignore"); +extern BOOL_VAR_H (textord_restore_underlines, FALSE, +"Chop underlines & put back"); +void restore_underlined_blobs( //get chop points + TO_BLOCK *block //block to do + ); +TO_ROW *most_overlapping_row( //find best row + TO_ROW_LIST *rows, //list of rows + BLOBNBOX *blob //blob to place + ); +void find_underlined_blobs( //get chop points + BLOBNBOX *u_line, //underlined unit + QSPLINE *baseline, //actual baseline + float xheight, //height of line + float baseline_offset, //amount to shrinke it + ICOORDELT_LIST *chop_cells //places to chop + ); +void vertical_cunderline_projection( //project outlines + C_OUTLINE *outline, //outline to project + QSPLINE *baseline, //actual baseline + float xheight, //height of line + float baseline_offset, //amount to shrinke it + STATS *lower_proj, //below baseline + STATS *middle_proj, //centre region + STATS *upper_proj //top region + ); +#endif diff --git a/textord/wordseg.cpp b/textord/wordseg.cpp new file mode 100644 index 0000000000..46c947cd28 --- /dev/null +++ b/textord/wordseg.cpp @@ -0,0 +1,620 @@ +/********************************************************************** + * File: wordseg.cpp (Formerly wspace.c) + * Description: Code to segment the blobs into words. + * Author: Ray Smith + * Created: Fri Oct 16 11:32:28 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include "stderr.h" +#include "blobbox.h" +#include "ocrclass.h" +#include "lmedsq.h" +#include "statistc.h" +#include "drawtord.h" +#include "makerow.h" +#include "pitsync1.h" +#include "blobcmpl.h" +#include "tovars.h" +#include "topitch.h" +#include "tospace.h" +#include "fpchop.h" +#include "wordseg.h" + +#define EXTERN + +EXTERN BOOL_VAR (textord_fp_chopping, TRUE, "Do fixed pitch chopping"); +extern /*"C" */ ETEXT_DESC *global_monitor; //progress monitor + +#define FIXED_WIDTH_MULTIPLE 5 +#define BLOCK_STATS_CLUSTERS 10 + +/********************************************************************** + * make_words + * + * Arrange the blobs into words. + **********************************************************************/ + +void make_words( //make words + ICOORD page_tr, //top right + float gradient, //page skew + BLOCK_LIST *blocks, //block list + TO_BLOCK_LIST *land_blocks, //rotated for landscape + TO_BLOCK_LIST *port_blocks //output list + ) { + TO_BLOCK_IT block_it; //iterator + TO_BLOCK *block; //current block; + + compute_fixed_pitch (page_tr, port_blocks, gradient, FCOORD (0.0f, -1.0f), + !(BOOL8) textord_test_landscape); + if (global_monitor != NULL) { + global_monitor->ocr_alive = TRUE; + global_monitor->progress = 25; + } + to_spacing(page_tr, port_blocks); + block_it.set_to_list (port_blocks); + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) { + block = block_it.data (); + // set_row_spaces(block,FCOORD(1,0),!(BOOL8)textord_test_landscape); + //make proper classes + make_real_words (block, FCOORD (1.0f, 0.0f)); + } +} + + +/********************************************************************** + * set_row_spaces + * + * Set the min_space and max_nonspace members of the row so that + * the blobs can be arranged into words. + **********************************************************************/ + +void set_row_spaces( //find space sizes + TO_BLOCK *block, //block to do + FCOORD rotation, //for drawing + BOOL8 testing_on //correct orientation + ) { + INT32 maxwidth; //of widest space + TO_ROW *row; //current row + TO_ROW_IT row_it = block->get_rows (); + + if (row_it.empty ()) + return; //empty block + maxwidth = (INT32) ceil (block->xheight * textord_words_maxspace); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->fixed_pitch == 0) { + // if (!textord_test_mode + // && row_words(block,row,maxwidth,rotation,testing_on)==0 + // || textord_test_mode + // && row_words2(block,row,maxwidth,rotation,testing_on)==0) + // { + row->min_space = + (INT32) ceil (row->pr_space - + (row->pr_space - + row->pr_nonsp) * textord_words_definite_spread); + row->max_nonspace = + (INT32) floor (row->pr_nonsp + + (row->pr_space - + row->pr_nonsp) * textord_words_definite_spread); + if (testing_on && textord_show_initial_words) { + tprintf ("Assigning defaults %d non, %d space to row at %g\n", + row->max_nonspace, row->min_space, row->intercept ()); + } + row->space_threshold = (row->max_nonspace + row->min_space) / 2; + row->space_size = row->pr_space; + row->kern_size = row->pr_nonsp; + // } + } +#ifndef GRAPHICS_DISABLED + if (textord_show_initial_words && testing_on) { + plot_word_decisions (to_win, (INT16) row->fixed_pitch, row); + } +#endif + } +} + + +/********************************************************************** + * row_words + * + * Compute the max nonspace and min space for the row. + **********************************************************************/ + +INT32 row_words( //compute space size + TO_BLOCK *block, //block it came from + TO_ROW *row, //row to operate on + INT32 maxwidth, //max expected space size + FCOORD rotation, //for drawing + BOOL8 testing_on //for debug + ) { + BOOL8 testing_row; //contains testpt + BOOL8 prev_valid; //if decent size + BOOL8 this_valid; //current blob big enough + INT32 prev_x; //end of prev blob + INT32 min_gap; //min interesting gap + INT32 cluster_count; //no of clusters + INT32 gap_index; //which cluster + INT32 smooth_factor; //for smoothing stats + BLOBNBOX *blob; //current blob + float lower, upper; //clustering parameters + float gaps[3]; //gap clusers + ICOORD testpt; + BOX blob_box; //bounding box + //iterator + BLOBNBOX_IT blob_it = row->blob_list (); + STATS gap_stats (0, maxwidth); + STATS cluster_stats[4]; //clusters + + testpt = ICOORD (textord_test_x, textord_test_y); + smooth_factor = + (INT32) (block->xheight * textord_wordstats_smooth_factor + 1.5); + // if (testing_on) + // tprintf("Row smooth factor=%d\n",smooth_factor); + prev_valid = FALSE; + prev_x = -MAX_INT32; + testing_row = FALSE; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + if (blob_box.contains (testpt)) + testing_row = TRUE; + gap_stats.add (blob_box.width (), 1); + } + min_gap = (INT32) floor (gap_stats.ile (textord_words_width_ile)); + gap_stats.clear (); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (!blob->joined_to_prev ()) { + blob_box = blob->bounding_box (); + // this_valid=blob_box.width()>=min_gap; + this_valid = TRUE; + if (this_valid && prev_valid + && blob_box.left () - prev_x < maxwidth) { + gap_stats.add (blob_box.left () - prev_x, 1); + } + prev_x = blob_box.right (); + prev_valid = this_valid; + } + } + if (gap_stats.get_total () == 0) { + row->min_space = 0; //no evidence + row->max_nonspace = 0; + return 0; + } + gap_stats.smooth (smooth_factor); + lower = row->xheight * textord_words_initial_lower; + upper = row->xheight * textord_words_initial_upper; + cluster_count = gap_stats.cluster (lower, upper, + textord_spacesize_ratioprop, 3, + cluster_stats); + while (cluster_count < 2 && ceil (lower) < floor (upper)) { + //shrink gap + upper = (upper * 3 + lower) / 4; + lower = (lower * 3 + upper) / 4; + cluster_count = gap_stats.cluster (lower, upper, + textord_spacesize_ratioprop, 3, + cluster_stats); + } + if (cluster_count < 2) { + row->min_space = 0; //no evidence + row->max_nonspace = 0; + return 0; + } + for (gap_index = 0; gap_index < cluster_count; gap_index++) + gaps[gap_index] = cluster_stats[gap_index + 1].ile (0.5); + //get medians + if (cluster_count > 2) { + if (testing_on && textord_show_initial_words) { + tprintf ("Row at %g has 3 sizes of gap:%g,%g,%g\n", + row->intercept (), + cluster_stats[1].ile (0.5), + cluster_stats[2].ile (0.5), cluster_stats[3].ile (0.5)); + } + lower = gaps[0]; + if (gaps[1] > lower) { + upper = gaps[1]; //prefer most frequent + if (upper < block->xheight * textord_words_min_minspace + && gaps[2] > gaps[1]) { + upper = gaps[2]; + } + } + else if (gaps[2] > lower + && gaps[2] >= block->xheight * textord_words_min_minspace) + upper = gaps[2]; + else if (lower >= block->xheight * textord_words_min_minspace) { + upper = lower; //not nice + lower = gaps[1]; + if (testing_on && textord_show_initial_words) { + tprintf ("Had to switch most common from lower to upper!!\n"); + gap_stats.print (stdout, TRUE); + } + } + else { + row->min_space = 0; //no evidence + row->max_nonspace = 0; + return 0; + } + } + else { + if (gaps[1] < gaps[0]) { + if (testing_on && textord_show_initial_words) { + tprintf ("Had to switch most common from lower to upper!!\n"); + gap_stats.print (stdout, TRUE); + } + lower = gaps[1]; + upper = gaps[0]; + } + else { + upper = gaps[1]; + lower = gaps[0]; + } + } + if (upper < block->xheight * textord_words_min_minspace) { + row->min_space = 0; //no evidence + row->max_nonspace = 0; + return 0; + } + if (upper * 3 < block->min_space * 2 + block->max_nonspace + || lower * 3 > block->min_space * 2 + block->max_nonspace) { + if (testing_on && textord_show_initial_words) { + tprintf ("Disagreement between block and row at %g!!\n", + row->intercept ()); + tprintf ("Lower=%g, upper=%g, Stats:\n", lower, upper); + gap_stats.print (stdout, TRUE); + } + } + row->min_space = + (INT32) ceil (upper - (upper - lower) * textord_words_definite_spread); + row->max_nonspace = + (INT32) floor (lower + (upper - lower) * textord_words_definite_spread); + row->space_threshold = (row->max_nonspace + row->min_space) / 2; + row->space_size = upper; + row->kern_size = lower; + if (testing_on && textord_show_initial_words) { + if (testing_row) { + tprintf ("GAP STATS\n"); + gap_stats.print (stdout, TRUE); + tprintf ("SPACE stats\n"); + cluster_stats[2].print (stdout, FALSE); + tprintf ("NONSPACE stats\n"); + cluster_stats[1].print (stdout, FALSE); + } + tprintf ("Row at %g has minspace=%d(%g), max_non=%d(%g)\n", + row->intercept (), row->min_space, upper, + row->max_nonspace, lower); + } + return cluster_stats[2].get_total (); +} + + +/********************************************************************** + * row_words2 + * + * Compute the max nonspace and min space for the row. + **********************************************************************/ + +INT32 row_words2( //compute space size + TO_BLOCK *block, //block it came from + TO_ROW *row, //row to operate on + INT32 maxwidth, //max expected space size + FCOORD rotation, //for drawing + BOOL8 testing_on //for debug + ) { + BOOL8 testing_row; //contains testpt + BOOL8 prev_valid; //if decent size + BOOL8 this_valid; //current blob big enough + INT32 prev_x; //end of prev blob + INT32 min_width; //min interesting width + INT32 valid_count; //good gaps + INT32 total_count; //total gaps + INT32 cluster_count; //no of clusters + INT32 prev_count; //previous cluster_count + INT32 gap_index; //which cluster + INT32 smooth_factor; //for smoothing stats + BLOBNBOX *blob; //current blob + float lower, upper; //clustering parameters + ICOORD testpt; + BOX blob_box; //bounding box + //iterator + BLOBNBOX_IT blob_it = row->blob_list (); + STATS gap_stats (0, maxwidth); + //gap sizes + float gaps[BLOCK_STATS_CLUSTERS]; + STATS cluster_stats[BLOCK_STATS_CLUSTERS + 1]; + //clusters + + testpt = ICOORD (textord_test_x, textord_test_y); + smooth_factor = + (INT32) (block->xheight * textord_wordstats_smooth_factor + 1.5); + // if (testing_on) + // tprintf("Row smooth factor=%d\n",smooth_factor); + prev_valid = FALSE; + prev_x = -MAX_INT16; + testing_row = FALSE; + //min blob size + min_width = (INT32) block->pr_space; + total_count = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (!blob->joined_to_prev ()) { + blob_box = blob->bounding_box (); + this_valid = blob_box.width () >= min_width; + this_valid = TRUE; + if (this_valid && prev_valid + && blob_box.left () - prev_x < maxwidth) { + gap_stats.add (blob_box.left () - prev_x, 1); + } + total_count++; //count possibles + prev_x = blob_box.right (); + prev_valid = this_valid; + } + } + valid_count = gap_stats.get_total (); + if (valid_count < total_count * textord_words_minlarge) { + gap_stats.clear (); + prev_x = -MAX_INT16; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + if (!blob->joined_to_prev ()) { + blob_box = blob->bounding_box (); + if (blob_box.left () - prev_x < maxwidth) { + gap_stats.add (blob_box.left () - prev_x, 1); + } + prev_x = blob_box.right (); + } + } + } + if (gap_stats.get_total () == 0) { + row->min_space = 0; //no evidence + row->max_nonspace = 0; + return 0; + } + + cluster_count = 0; + lower = block->xheight * words_initial_lower; + upper = block->xheight * words_initial_upper; + gap_stats.smooth (smooth_factor); + do { + prev_count = cluster_count; + cluster_count = gap_stats.cluster (lower, upper, + textord_spacesize_ratioprop, + BLOCK_STATS_CLUSTERS, cluster_stats); + } + while (cluster_count > prev_count && cluster_count < BLOCK_STATS_CLUSTERS); + if (cluster_count < 1) { + row->min_space = 0; + row->max_nonspace = 0; + return 0; + } + for (gap_index = 0; gap_index < cluster_count; gap_index++) + gaps[gap_index] = cluster_stats[gap_index + 1].ile (0.5); + //get medians + if (testing_on) { + tprintf ("cluster_count=%d:", cluster_count); + for (gap_index = 0; gap_index < cluster_count; gap_index++) + tprintf (" %g(%d)", gaps[gap_index], + cluster_stats[gap_index + 1].get_total ()); + tprintf ("\n"); + } + + //Try to find proportional non-space and space for row. + for (gap_index = 0; gap_index < cluster_count + && gaps[gap_index] > block->max_nonspace; gap_index++); + if (gap_index < cluster_count) + lower = gaps[gap_index]; //most frequent below + else { + if (testing_on) + tprintf ("No cluster below block threshold!, using default=%g\n", + block->pr_nonsp); + lower = block->pr_nonsp; + } + for (gap_index = 0; gap_index < cluster_count + && gaps[gap_index] <= block->max_nonspace; gap_index++); + if (gap_index < cluster_count) + upper = gaps[gap_index]; //most frequent above + else { + if (testing_on) + tprintf ("No cluster above block threshold!, using default=%g\n", + block->pr_space); + upper = block->pr_space; + } + row->min_space = + (INT32) ceil (upper - (upper - lower) * textord_words_definite_spread); + row->max_nonspace = + (INT32) floor (lower + (upper - lower) * textord_words_definite_spread); + row->space_threshold = (row->max_nonspace + row->min_space) / 2; + row->space_size = upper; + row->kern_size = lower; + if (testing_on) { + if (testing_row) { + tprintf ("GAP STATS\n"); + gap_stats.print (stdout, TRUE); + tprintf ("SPACE stats\n"); + cluster_stats[2].print (stdout, FALSE); + tprintf ("NONSPACE stats\n"); + cluster_stats[1].print (stdout, FALSE); + } + tprintf ("Row at %g has minspace=%d(%g), max_non=%d(%g)\n", + row->intercept (), row->min_space, upper, + row->max_nonspace, lower); + } + return 1; +} + + +/********************************************************************** + * make_real_words + * + * Convert a TO_BLOCK to a BLOCK. + **********************************************************************/ + +void make_real_words( //find lines + TO_BLOCK *block, //block to do + FCOORD rotation //for drawing + ) { + TO_ROW *row; //current row + TO_ROW_IT row_it = block->get_rows (); + ROW *real_row = NULL; //output row + ROW_IT real_row_it = block->block->row_list (); + + if (row_it.empty ()) + return; //empty block + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->blob_list ()->empty () && !row->rep_words.empty ()) { + real_row = make_rep_words (row, block); + } + else if (!row->blob_list ()->empty ()) { + // tprintf("Row pitch_decision=%d",row->pitch_decision); + if (row->pitch_decision == PITCH_DEF_FIXED + || row->pitch_decision == PITCH_CORR_FIXED) + real_row = fixed_pitch_words (row, rotation); + else if (row->pitch_decision == PITCH_DEF_PROP + || row->pitch_decision == PITCH_CORR_PROP) + real_row = make_prop_words (row, rotation); + else + ASSERT_HOST(FALSE); + } + if (real_row != NULL) { + //put row in block + real_row_it.add_after_then_move (real_row); + } + } + block->block->set_stats (block->fixed_pitch == 0, (INT16) block->kern_size, + (INT16) block->space_size, + (INT16) block->fixed_pitch); + block->block->check_pitch (); +} + + +/********************************************************************** + * make_rep_words + * + * Fabricate a real row from only the repeated blob words. + * Get the xheight from the block as it may be more meaningful. + **********************************************************************/ + +ROW *make_rep_words( //make a row + TO_ROW *row, //row to convert + TO_BLOCK *block //block it lives in + ) { + INT32 xstarts[2]; //ends of row + ROW *real_row; //output row + BOX word_box; //bounding box + double coeffs[3]; //spline + //iterator + WERD_IT word_it = &row->rep_words; + + if (word_it.empty ()) + return NULL; + word_box = word_it.data ()->bounding_box (); + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) + word_box += word_it.data ()->bounding_box (); + xstarts[0] = word_box.left (); + xstarts[1] = word_box.right (); + coeffs[0] = 0; + coeffs[1] = row->line_m (); + coeffs[2] = row->line_c (); + row->xheight = block->xheight; + real_row = new ROW (row, + (INT16) block->kern_size, (INT16) block->space_size); + word_it.set_to_list (real_row->word_list ()); + //put words in row + word_it.add_list_after (&row->rep_words); + real_row->recalc_bounding_box (); + return real_row; +} + + +/********************************************************************** + * make_real_word + * + * Construct a WERD from a given number of adjacent entries in a + * list of BLOBNBOXs. + **********************************************************************/ + +WERD *make_real_word( //make a WERD + BLOBNBOX_IT *box_it, //iterator + INT32 blobcount, //no of blobs to use + BOOL8 bol, //start of line + BOOL8 fuzzy_sp, //fuzzy space + BOOL8 fuzzy_non, //fuzzy non-space + UINT8 blanks //no of blanks + ) { + OUTLINE_IT out_it; //outlines + C_OUTLINE_IT cout_it; + PBLOB_LIST blobs; //blobs in word + C_BLOB_LIST cblobs; + PBLOB_IT blob_it = &blobs; //iterator + C_BLOB_IT cblob_it = &cblobs; + WERD *word; //new word + BLOBNBOX *bblob; //current blob + INT32 blobindex; //in row + + for (blobindex = 0; blobindex < blobcount; blobindex++) { + bblob = box_it->extract (); + if (bblob->joined_to_prev ()) { + if (bblob->blob () != NULL) { + out_it.set_to_list (blob_it.data ()->out_list ()); + out_it.move_to_last (); + out_it.add_list_after (bblob->blob ()->out_list ()); + delete bblob->blob (); + } + else if (bblob->cblob () != NULL) { + cout_it.set_to_list (cblob_it.data ()->out_list ()); + cout_it.move_to_last (); + cout_it.add_list_after (bblob->cblob ()->out_list ()); + delete bblob->cblob (); + } + } + else { + if (bblob->blob () != NULL) + blob_it.add_after_then_move (bblob->blob ()); + else if (bblob->cblob () != NULL) + cblob_it.add_after_then_move (bblob->cblob ()); + } + delete bblob; + box_it->forward (); //next one + } + + if (blanks < 1) + blanks = 1; + if (!blob_it.empty ()) { + //make real word + word = new WERD (&blobs, blanks, NULL); + } + else { + word = new WERD (&cblobs, blanks, NULL); + } + if (bol) { + word->set_flag (W_BOL, TRUE); + } + if (fuzzy_sp) + //probably space + word->set_flag (W_FUZZY_SP, TRUE); + else if (fuzzy_non) + //probably not + word->set_flag (W_FUZZY_NON, TRUE); + if (box_it->at_first ()) { + word->set_flag (W_EOL, TRUE);//at end of line + } + return word; +} diff --git a/textord/wordseg.h b/textord/wordseg.h new file mode 100644 index 0000000000..56a9763a00 --- /dev/null +++ b/textord/wordseg.h @@ -0,0 +1,70 @@ +/********************************************************************** + * File: wordseg.h (Formerly wspace.h) + * Description: Code to segment the blobs into words. + * Author: Ray Smith + * Created: Fri Oct 16 11:32:28 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef WORDSEG_H +#define WORDSEG_H + +#include "varable.h" +#include "blobbox.h" +#include "notdll.h" + +extern BOOL_VAR_H (textord_fp_chopping, TRUE, "Do fixed pitch chopping"); +void make_words( //make words + ICOORD page_tr, //top right + float gradient, //page skew + BLOCK_LIST *blocks, //block list + TO_BLOCK_LIST *land_blocks, //rotated for landscape + TO_BLOCK_LIST *port_blocks //output list + ); +void set_row_spaces( //find space sizes + TO_BLOCK *block, //block to do + FCOORD rotation, //for drawing + BOOL8 testing_on //correct orientation + ); +INT32 row_words( //compute space size + TO_BLOCK *block, //block it came from + TO_ROW *row, //row to operate on + INT32 maxwidth, //max expected space size + FCOORD rotation, //for drawing + BOOL8 testing_on //for debug + ); +INT32 row_words2( //compute space size + TO_BLOCK *block, //block it came from + TO_ROW *row, //row to operate on + INT32 maxwidth, //max expected space size + FCOORD rotation, //for drawing + BOOL8 testing_on //for debug + ); +void make_real_words( //find lines + TO_BLOCK *block, //block to do + FCOORD rotation //for drawing + ); +ROW *make_rep_words( //make a row + TO_ROW *row, //row to convert + TO_BLOCK *block //block it lives in + ); +WERD *make_real_word( //make a WERD + BLOBNBOX_IT *box_it, //iterator + INT32 blobcount, //no of blobs to use + BOOL8 bol, //start of line + BOOL8 fuzzy_sp, //fuzzy space + BOOL8 fuzzy_non, //fuzzy non-space + UINT8 blanks //no of blanks + ); +#endif diff --git a/training/Makefile.am b/training/Makefile.am new file mode 100644 index 0000000000..34a00ec531 --- /dev/null +++ b/training/Makefile.am @@ -0,0 +1,40 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer \ + -I$(top_srcdir)/ccops -I$(top_srcdir)/dict \ + -I$(top_srcdir)/classify -I$(top_srcdir)/display \ + -I$(top_srcdir)/wordrec -I$(top_srcdir)/cutil \ + -I$(top_srcdir)/textord + +EXTRA_DIST = \ + cnTraining.dsp mfTraining.dsp \ + mergenf.h name2char.h training.h + +noinst_LIBRARIES = libtesseract_training.a +libtesseract_training_a_SOURCES = \ + name2char.cpp training.cpp + +bin_PROGRAMS = cntraining mftraining +cntraining_SOURCES = cnTraining.cpp +cntraining_LDADD = \ + libtesseract_training.a \ + ../textord/libtesseract_textord.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a \ + ../image/libtesseract_image.a \ + ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../viewer/libtesseract_viewer.a \ + ../ccutil/libtesseract_ccutil.a +mftraining_SOURCES = mfTraining.cpp mergenf.cpp +mftraining_LDADD = \ + libtesseract_training.a \ + ../textord/libtesseract_textord.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a \ + ../image/libtesseract_image.a \ + ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../viewer/libtesseract_viewer.a \ + ../ccutil/libtesseract_ccutil.a diff --git a/training/Makefile.in b/training/Makefile.in new file mode 100644 index 0000000000..04e7ffeee9 --- /dev/null +++ b/training/Makefile.in @@ -0,0 +1,624 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = cntraining$(EXEEXT) mftraining$(EXEEXT) +subdir = training +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_training_a_AR = $(AR) $(ARFLAGS) +libtesseract_training_a_LIBADD = +am_libtesseract_training_a_OBJECTS = name2char.$(OBJEXT) \ + training.$(OBJEXT) +libtesseract_training_a_OBJECTS = \ + $(am_libtesseract_training_a_OBJECTS) +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_cntraining_OBJECTS = cnTraining.$(OBJEXT) +cntraining_OBJECTS = $(am_cntraining_OBJECTS) +cntraining_DEPENDENCIES = libtesseract_training.a \ + ../textord/libtesseract_textord.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a ../image/libtesseract_image.a \ + ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../viewer/libtesseract_viewer.a \ + ../ccutil/libtesseract_ccutil.a +am_mftraining_OBJECTS = mfTraining.$(OBJEXT) mergenf.$(OBJEXT) +mftraining_OBJECTS = $(am_mftraining_OBJECTS) +mftraining_DEPENDENCIES = libtesseract_training.a \ + ../textord/libtesseract_textord.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a ../image/libtesseract_image.a \ + ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../viewer/libtesseract_viewer.a \ + ../ccutil/libtesseract_ccutil.a +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_training_a_SOURCES) $(cntraining_SOURCES) \ + $(mftraining_SOURCES) +DIST_SOURCES = $(libtesseract_training_a_SOURCES) \ + $(cntraining_SOURCES) $(mftraining_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer \ + -I$(top_srcdir)/ccops -I$(top_srcdir)/dict \ + -I$(top_srcdir)/classify -I$(top_srcdir)/display \ + -I$(top_srcdir)/wordrec -I$(top_srcdir)/cutil \ + -I$(top_srcdir)/textord + +EXTRA_DIST = \ + cnTraining.dsp mfTraining.dsp \ + mergenf.h name2char.h training.h + +noinst_LIBRARIES = libtesseract_training.a +libtesseract_training_a_SOURCES = \ + name2char.cpp training.cpp + +cntraining_SOURCES = cnTraining.cpp +cntraining_LDADD = \ + libtesseract_training.a \ + ../textord/libtesseract_textord.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a \ + ../image/libtesseract_image.a \ + ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../viewer/libtesseract_viewer.a \ + ../ccutil/libtesseract_ccutil.a + +mftraining_SOURCES = mfTraining.cpp mergenf.cpp +mftraining_LDADD = \ + libtesseract_training.a \ + ../textord/libtesseract_textord.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a \ + ../image/libtesseract_image.a \ + ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../viewer/libtesseract_viewer.a \ + ../ccutil/libtesseract_ccutil.a + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu training/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu training/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_training.a: $(libtesseract_training_a_OBJECTS) $(libtesseract_training_a_DEPENDENCIES) + -rm -f libtesseract_training.a + $(libtesseract_training_a_AR) libtesseract_training.a $(libtesseract_training_a_OBJECTS) $(libtesseract_training_a_LIBADD) + $(RANLIB) libtesseract_training.a +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +cntraining$(EXEEXT): $(cntraining_OBJECTS) $(cntraining_DEPENDENCIES) + @rm -f cntraining$(EXEEXT) + $(CXXLINK) $(cntraining_LDFLAGS) $(cntraining_OBJECTS) $(cntraining_LDADD) $(LIBS) +mftraining$(EXEEXT): $(mftraining_OBJECTS) $(mftraining_DEPENDENCIES) + @rm -f mftraining$(EXEEXT) + $(CXXLINK) $(mftraining_LDFLAGS) $(mftraining_OBJECTS) $(mftraining_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cnTraining.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mergenf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfTraining.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/name2char.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/training.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ + clean-recursive ctags ctags-recursive distclean \ + distclean-compile distclean-generic distclean-recursive \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic maintainer-clean-recursive \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am uninstall-binPROGRAMS uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/training/cnTraining.cpp b/training/cnTraining.cpp new file mode 100644 index 0000000000..fdec476863 --- /dev/null +++ b/training/cnTraining.cpp @@ -0,0 +1,832 @@ +/****************************************************************************** +** Filename: cnTraining.cpp +** Purpose: Generates a normproto and pffmtable. +** Author: Dan Johnson +** Revisment: Christy Russon +** History: Fri Aug 18 08:53:50 1989, DSJ, Created. +** 5/25/90, DSJ, Adapted to multiple feature types. +** Tuesday, May 17, 1998 Changes made to make feature specific and +** simplify structures. First step in simplifying training process. +** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. +******************************************************************************/ + + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "oldlist.h" +#include "efio.h" +#include "emalloc.h" +#include "featdefs.h" +#include "getopt.h" +#include "ocrfeatures.h" +#include "general.h" +#include "clusttool.h" +#include "cluster.h" +#include "name2char.h" +#include +#include +#include + +#define MAXNAMESIZE 80 +#define MAX_NUM_SAMPLES 10000 +#define PROGRAM_FEATURE_TYPE "cn" +#define MINSD (1.0f / 64.0f) + +int row_number; /* cjn: fixes link problem */ + +typedef struct +{ + char *Label; + LIST List; +} +LABELEDLISTNODE, *LABELEDLIST; + +#define round(x,frag)(floor(x/frag+.5)*frag) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +int main ( + int argc, + char **argv); + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +void ParseArguments( + int argc, + char **argv); + +char *GetNextFilename (); + +void ReadTrainingSamples ( + FILE *File, + LIST* TrainingSamples); + +LABELEDLIST FindList ( + LIST List, + char *Label); + +LABELEDLIST NewLabeledList ( + char *Label); + +void WriteTrainingSamples ( + char *Directory, + LIST CharList); + +void WriteNormProtos ( + char *Directory, + LIST LabeledProtoList, + CLUSTERER *Clusterer); + +void FreeTrainingSamples ( + LIST CharList); + +void FreeNormProtoList ( + LIST CharList); + +void FreeLabeledList ( + LABELEDLIST LabeledList); + +CLUSTERER *SetUpForClustering( + LABELEDLIST CharSample); +/* +PARAMDESC *ConvertToPARAMDESC( + PARAM_DESC* Param_Desc, + int N); +*/ +void AddToNormProtosList( + LIST* NormProtoList, + LIST ProtoList, + char* CharName); + +void WriteProtos( + FILE *File, + UINT16 N, + LIST ProtoList, + BOOL8 WriteSigProtos, + BOOL8 WriteInsigProtos); + +int NumberOfProtos( + LIST ProtoList, + BOOL8 CountSigProtos, + BOOL8 CountInsigProtos); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +static char FontName[MAXNAMESIZE]; +/* globals used for parsing command line arguments */ +static char *Directory = NULL; +static int MaxNumSamples = MAX_NUM_SAMPLES; +static int Argc; +static char **Argv; + +/* globals used to control what information is saved in the output file */ +static BOOL8 ShowAllSamples = FALSE; +static BOOL8 ShowSignificantProtos = TRUE; +static BOOL8 ShowInsignificantProtos = FALSE; + +/* global variable to hold configuration parameters to control clustering */ +//-M 0.025 -B 0.05 -I 0.8 -C 1e-3 +static CLUSTERCONFIG Config = +{ + elliptical, 0.025, 0.05, 0.8, 1e-3 +}; + +static FLOAT32 RoundingAccuracy = 0.0; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int main ( + int argc, + char **argv) + +/* +** Parameters: +** argc number of command line arguments +** argv array of command line arguments +** Globals: none +** Operation: +** This program reads in a text file consisting of feature +** samples from a training page in the following format: +** +** FontName CharName NumberOfFeatureTypes(N) +** FeatureTypeName1 NumberOfFeatures(M) +** Feature1 +** ... +** FeatureM +** FeatureTypeName2 NumberOfFeatures(M) +** Feature1 +** ... +** FeatureM +** ... +** FeatureTypeNameN NumberOfFeatures(M) +** Feature1 +** ... +** FeatureM +** FontName CharName ... +** +** It then appends these samples into a separate file for each +** character. The name of the file is +** +** DirectoryName/FontName/CharName.FeatureTypeName +** +** The DirectoryName can be specified via a command +** line argument. If not specified, it defaults to the +** current directory. The format of the resulting files is: +** +** NumberOfFeatures(M) +** Feature1 +** ... +** FeatureM +** NumberOfFeatures(M) +** ... +** +** The output files each have a header which describes the +** type of feature which the file contains. This header is +** in the format required by the clusterer. A command line +** argument can also be used to specify that only the first +** N samples of each class should be used. +** Return: none +** Exceptions: none +** History: Fri Aug 18 08:56:17 1989, DSJ, Created. +*/ + +{ + char *PageName; + FILE *TrainingPage; + LIST CharList = NIL; + CLUSTERER *Clusterer = NULL; + LIST ProtoList = NIL; + LIST NormProtoList = NIL; + LIST pCharList; + LABELEDLIST CharSample; + + ParseArguments (argc, argv); + while ((PageName = GetNextFilename()) != NULL) + { + printf ("\nReading %s ...", PageName); + TrainingPage = Efopen (PageName, "r"); + ReadTrainingSamples (TrainingPage, &CharList); + fclose (TrainingPage); + //WriteTrainingSamples (Directory, CharList); + } + pCharList = CharList; + iterate(pCharList) + { + //Cluster + CharSample = (LABELEDLIST) first (pCharList); + printf ("\nClustering %s ...", CharSample->Label); + Clusterer = SetUpForClustering(CharSample); + ProtoList = ClusterSamples(Clusterer, &Config); + AddToNormProtosList(&NormProtoList, ProtoList, CharSample->Label); + } + FreeTrainingSamples (CharList); + WriteNormProtos (Directory, NormProtoList, Clusterer); + FreeClusterer(Clusterer); + FreeProtoList(&ProtoList); + FreeNormProtoList(NormProtoList); + printf ("\n"); + return 0; +} // main + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ParseArguments( + int argc, + char **argv) + +/* +** Parameters: +** argc number of command line arguments to parse +** argv command line arguments +** Globals: +** ShowAllSamples flag controlling samples display +** ShowSignificantProtos flag controlling proto display +** ShowInsignificantProtos flag controlling proto display +** Config current clustering parameters +** optarg, optind defined by getopt sys call +** Argc, Argv global copies of argc and argv +** Operation: +** This routine parses the command line arguments that were +** passed to the program. The legal arguments are: +** -d "turn off display of samples" +** -p "turn off significant protos" +** -n "turn off insignificant proto" +** -S [ spherical | elliptical | mixed | automatic ] +** -M MinSamples "min samples per prototype (%)" +** -B MaxIllegal "max illegal chars per cluster (%)" +** -I Independence "0 to 1" +** -C Confidence "1e-200 to 1.0" +** -D Directory +** -N MaxNumSamples +** -R RoundingAccuracy +** Return: none +** Exceptions: Illegal options terminate the program. +** History: 7/24/89, DSJ, Created. +*/ + +{ + int Option; + int ParametersRead; + BOOL8 Error; + extern char *optarg; + + Error = FALSE; + Argc = argc; + Argv = argv; + while (( Option = getopt( argc, argv, "R:N:D:C:I:M:B:S:d:n:p" )) != EOF ) + { + switch ( Option ) + { + case 'n': + sscanf(optarg,"%d", &ParametersRead); + ShowInsignificantProtos = ParametersRead; + break; + case 'p': + sscanf(optarg,"%d", &ParametersRead); + ShowSignificantProtos = ParametersRead; + break; + case 'd': + ShowAllSamples = FALSE; + break; + case 'C': + ParametersRead = sscanf( optarg, "%lf", &(Config.Confidence) ); + if ( ParametersRead != 1 ) Error = TRUE; + else if ( Config.Confidence > 1 ) Config.Confidence = 1; + else if ( Config.Confidence < 0 ) Config.Confidence = 0; + break; + case 'I': + ParametersRead = sscanf( optarg, "%f", &(Config.Independence) ); + if ( ParametersRead != 1 ) Error = TRUE; + else if ( Config.Independence > 1 ) Config.Independence = 1; + else if ( Config.Independence < 0 ) Config.Independence = 0; + break; + case 'M': + ParametersRead = sscanf( optarg, "%f", &(Config.MinSamples) ); + if ( ParametersRead != 1 ) Error = TRUE; + else if ( Config.MinSamples > 1 ) Config.MinSamples = 1; + else if ( Config.MinSamples < 0 ) Config.MinSamples = 0; + break; + case 'B': + ParametersRead = sscanf( optarg, "%f", &(Config.MaxIllegal) ); + if ( ParametersRead != 1 ) Error = TRUE; + else if ( Config.MaxIllegal > 1 ) Config.MaxIllegal = 1; + else if ( Config.MaxIllegal < 0 ) Config.MaxIllegal = 0; + break; + case 'R': + ParametersRead = sscanf( optarg, "%f", &RoundingAccuracy ); + if ( ParametersRead != 1 ) Error = TRUE; + else if ( RoundingAccuracy > 0.01 ) RoundingAccuracy = 0.01; + else if ( RoundingAccuracy < 0.0 ) RoundingAccuracy = 0.0; + break; + case 'S': + switch ( optarg[0] ) + { + case 's': Config.ProtoStyle = spherical; break; + case 'e': Config.ProtoStyle = elliptical; break; + case 'm': Config.ProtoStyle = mixed; break; + case 'a': Config.ProtoStyle = automatic; break; + default: Error = TRUE; + } + break; + case 'D': + Directory = optarg; + break; + case 'N': + if (sscanf (optarg, "%d", &MaxNumSamples) != 1 || + MaxNumSamples <= 0) + Error = TRUE; + break; + case '?': + Error = TRUE; + break; + } + if ( Error ) + { + fprintf (stderr, "usage: %s [-D] [-P] [-N]\n", argv[0] ); + fprintf (stderr, "\t[-S ProtoStyle]\n"); + fprintf (stderr, "\t[-M MinSamples] [-B MaxBad] [-I Independence] [-C Confidence]\n" ); + fprintf (stderr, "\t[-d directory] [-n MaxNumSamples] [ TrainingPage ... ]\n"); + exit (2); + } + } +} /* ParseArguments */ + +/*---------------------------------------------------------------------------*/ +char *GetNextFilename () +/* +** Parameters: none +** Globals: +** optind defined by getopt sys call +** Argc, Argv global copies of argc and argv +** Operation: +** This routine returns the next command line argument. If +** there are no remaining command line arguments, it returns +** NULL. This routine should only be called after all option +** arguments have been parsed and removed with ParseArguments. +** Return: Next command line argument or NULL. +** Exceptions: none +** History: Fri Aug 18 09:34:12 1989, DSJ, Created. +*/ + +{ + if (optind < Argc) + return (Argv [optind++]); + else + return (NULL); + +} /* GetNextFilename */ + +/*---------------------------------------------------------------------------*/ +void ReadTrainingSamples ( + FILE *File, + LIST* TrainingSamples) + +/* +** Parameters: +** File open text file to read samples from +** Globals: none +** Operation: +** This routine reads training samples from a file and +** places them into a data structure which organizes the +** samples by FontName and CharName. It then returns this +** data structure. +** Return: none +** Exceptions: none +** History: Fri Aug 18 13:11:39 1989, DSJ, Created. +** Tue May 17 1998 simplifications to structure, illiminated +** font, and feature specification levels of structure. +*/ + +{ + char CharName[MAXNAMESIZE]; + LABELEDLIST CharSample; + FEATURE_SET FeatureSamples; + CHAR_DESC CharDesc; + int Type, i; + + while (fscanf (File, "%s %s", FontName, CharName) == 2) { + CharSample = FindList (*TrainingSamples, CharName); + if (CharSample == NULL) { + CharSample = NewLabeledList (CharName); + *TrainingSamples = push (*TrainingSamples, CharSample); + } + CharDesc = ReadCharDescription (File); + Type = ShortNameToFeatureType(PROGRAM_FEATURE_TYPE); + FeatureSamples = FeaturesOfType(CharDesc, Type); + for (int feature = 0; feature < FeatureSamples->NumFeatures; ++feature) { + FEATURE f = FeatureSamples->Features[feature]; + for (int dim =0; dim < f->Type->NumParams; ++dim) + f->Params[dim] += UniformRandomNumber(-MINSD, MINSD); + } + CharSample->List = push (CharSample->List, FeatureSamples); + for (i = 0; i < NumFeatureSetsIn (CharDesc); i++) + if (Type != i) + FreeFeatureSet (FeaturesOfType (CharDesc, i)); + free (CharDesc); + } +} // ReadTrainingSamples + +/*---------------------------------------------------------------------------*/ +LABELEDLIST FindList ( + LIST List, + char *Label) + +/* +** Parameters: +** List list to search +** Label label to search for +** Globals: none +** Operation: +** This routine searches thru a list of labeled lists to find +** a list with the specified label. If a matching labeled list +** cannot be found, NULL is returned. +** Return: Labeled list with the specified Label or NULL. +** Exceptions: none +** History: Fri Aug 18 15:57:41 1989, DSJ, Created. +*/ + +{ + LABELEDLIST LabeledList; + + iterate (List) + { + LabeledList = (LABELEDLIST) first (List); + if (strcmp (LabeledList->Label, Label) == 0) + return (LabeledList); + } + return (NULL); + +} /* FindList */ + +/*---------------------------------------------------------------------------*/ +LABELEDLIST NewLabeledList ( + char *Label) + +/* +** Parameters: +** Label label for new list +** Globals: none +** Operation: +** This routine allocates a new, empty labeled list and gives +** it the specified label. +** Return: New, empty labeled list. +** Exceptions: none +** History: Fri Aug 18 16:08:46 1989, DSJ, Created. +*/ + +{ + LABELEDLIST LabeledList; + + LabeledList = (LABELEDLIST) (char*)Emalloc (sizeof (LABELEDLISTNODE)); + LabeledList->Label = (char*)Emalloc (strlen (Label)+1); + strcpy (LabeledList->Label, Label); + LabeledList->List = NIL; + return (LabeledList); + +} /* NewLabeledList */ + +/*---------------------------------------------------------------------------*/ +void WriteTrainingSamples ( + char *Directory, + LIST CharList) + +/* +** Parameters: +** Directory directory to place sample files into +** FontList list of fonts used in the training samples +** Globals: +** MaxNumSamples max number of samples per class to write +** Operation: +** This routine writes the specified samples into files which +** are organized according to the font name and character name +** of the samples. +** Return: none +** Exceptions: none +** History: Fri Aug 18 16:17:06 1989, DSJ, Created. +*/ + +{ + LABELEDLIST CharSample; + FEATURE_SET FeatureSet; + LIST FeatureList; + FILE *File; + char Filename[MAXNAMESIZE]; + int NumSamples; + + iterate (CharList) // iterate thru all of the fonts + { + CharSample = (LABELEDLIST) first (CharList); + + // construct the full pathname for the current samples file + strcpy (Filename, ""); + if (Directory != NULL) + { + strcat (Filename, Directory); + strcat (Filename, "/"); + } + strcat (Filename, "Merged"); + strcat (Filename, "/"); + strcat (Filename, CharSample->Label); + strcat (Filename, "."); + strcat (Filename, PROGRAM_FEATURE_TYPE); + printf ("\nWriting %s ...", Filename); + + /* if file does not exist, create a new one with an appropriate + header; otherwise append samples to the existing file */ + File = fopen (Filename, "r"); + if (File == NULL) + { + File = Efopen (Filename, "w"); + WriteOldParamDesc + (File, DefinitionOf (ShortNameToFeatureType (PROGRAM_FEATURE_TYPE))); + } + else + { + fclose (File); + File = Efopen (Filename, "a"); + } + + // append samples onto the file + FeatureList = CharSample->List; + NumSamples = 0; + iterate (FeatureList) + { + //if (NumSamples >= MaxNumSamples) break; + + FeatureSet = (FEATURE_SET) first (FeatureList); + WriteFeatureSet (File, FeatureSet); + NumSamples++; + } + fclose (File); + } +} /* WriteTrainingSamples */ + + +/*----------------------------------------------------------------------------*/ +void WriteNormProtos ( + char *Directory, + LIST LabeledProtoList, + CLUSTERER *Clusterer) + +/* +** Parameters: +** Directory directory to place sample files into +** Globals: +** MaxNumSamples max number of samples per class to write +** Operation: +** This routine writes the specified samples into files which +** are organized according to the font name and character name +** of the samples. +** Return: none +** Exceptions: none +** History: Fri Aug 18 16:17:06 1989, DSJ, Created. +*/ + +{ + FILE *File; + char Filename[MAXNAMESIZE]; + LABELEDLIST LabeledProto; + int N; + char Label; + + strcpy (Filename, ""); + if (Directory != NULL) + { + strcat (Filename, Directory); + strcat (Filename, "/"); + } + strcat (Filename, "normproto"); + printf ("\nWriting %s ...", Filename); + File = Efopen (Filename, "w"); + fprintf(File,"%0d\n",Clusterer->SampleSize); + WriteParamDesc(File,Clusterer->SampleSize,Clusterer->ParamDesc); + iterate(LabeledProtoList) + { + LabeledProto = (LABELEDLIST) first (LabeledProtoList); + N = NumberOfProtos(LabeledProto->List, + ShowSignificantProtos, ShowInsignificantProtos); + Label = NameToChar(LabeledProto->Label); + fprintf(File, "\n%c %d\n", Label, N); + WriteProtos(File, Clusterer->SampleSize, LabeledProto->List, + ShowSignificantProtos, ShowInsignificantProtos); + } + fclose (File); + +} // WriteNormProtos + +/*---------------------------------------------------------------------------*/ +void FreeTrainingSamples ( + LIST CharList) + +/* +** Parameters: +** FontList list of all fonts in document +** Globals: none +** Operation: +** This routine deallocates all of the space allocated to +** the specified list of training samples. +** Return: none +** Exceptions: none +** History: Fri Aug 18 17:44:27 1989, DSJ, Created. +*/ + +{ + LABELEDLIST CharSample; + FEATURE_SET FeatureSet; + LIST FeatureList; + + + printf ("\nFreeTrainingSamples..."); + iterate (CharList) /* iterate thru all of the fonts */ + { + CharSample = (LABELEDLIST) first (CharList); + FeatureList = CharSample->List; + iterate (FeatureList) /* iterate thru all of the classes */ + { + FeatureSet = (FEATURE_SET) first (FeatureList); + FreeFeatureSet (FeatureSet); + } + FreeLabeledList (CharSample); + } + destroy (CharList); + +} /* FreeTrainingSamples */ + +/*-------------------------------------------------------------------------*/ +void FreeNormProtoList ( + LIST CharList) + +{ + LABELEDLIST CharSample; + + iterate (CharList) /* iterate thru all of the fonts */ + { + CharSample = (LABELEDLIST) first (CharList); + FreeLabeledList (CharSample); + } + destroy (CharList); + +} // FreeNormProtoList + +/*---------------------------------------------------------------------------*/ +void FreeLabeledList ( + LABELEDLIST LabeledList) + +/* +** Parameters: +** LabeledList labeled list to be freed +** Globals: none +** Operation: +** This routine deallocates all of the memory consumed by +** a labeled list. It does not free any memory which may be +** consumed by the items in the list. +** Return: none +** Exceptions: none +** History: Fri Aug 18 17:52:45 1989, DSJ, Created. +*/ + +{ + destroy (LabeledList->List); + free (LabeledList->Label); + free (LabeledList); + +} /* FreeLabeledList */ + +/*---------------------------------------------------------------------------*/ +CLUSTERER *SetUpForClustering( + LABELEDLIST CharSample) + +/* +** Parameters: +** CharSample: LABELEDLIST that holds all the feature information for a +** given character. +** Globals: +** None +** Operation: +** This routine reads samples from a LABELEDLIST and enters +** those samples into a clusterer data structure. This +** data structure is then returned to the caller. +** Return: +** Pointer to new clusterer data structure. +** Exceptions: +** None +** History: +** 8/16/89, DSJ, Created. +*/ + +{ + UINT16 N; + int i, j; + FLOAT32 *Sample = NULL; + CLUSTERER *Clusterer; + INT32 CharID; + LIST FeatureList = NULL; + FEATURE_SET FeatureSet = NULL; + FEATURE_DESC FeatureDesc = NULL; +// PARAM_DESC* ParamDesc; + + FeatureDesc = DefinitionOf(ShortNameToFeatureType(PROGRAM_FEATURE_TYPE)); + N = FeatureDesc->NumParams; + //ParamDesc = ConvertToPARAMDESC(FeatureDesc->ParamDesc, N); + Clusterer = MakeClusterer(N,FeatureDesc->ParamDesc); +// free(ParamDesc); + + FeatureList = CharSample->List; + CharID = 0; + iterate(FeatureList) + { + FeatureSet = (FEATURE_SET) first (FeatureList); + for (i=0; i < FeatureSet->MaxNumFeatures; i++) + { + if (Sample == NULL) + Sample = (FLOAT32 *)Emalloc(N * sizeof(FLOAT32)); + for (j=0; j < N; j++) + if (RoundingAccuracy != 0.0) + Sample[j] = round(FeatureSet->Features[i]->Params[j], RoundingAccuracy); + else + Sample[j] = FeatureSet->Features[i]->Params[j]; + MakeSample (Clusterer, Sample, CharID); + } + CharID++; + } + if ( Sample != NULL ) free( Sample ); + return( Clusterer ); + +} /* SetUpForClustering */ + +/*---------------------------------------------------------------------------*/ +void AddToNormProtosList( + LIST* NormProtoList, + LIST ProtoList, + char* CharName) +{ + PROTOTYPE* Proto; + LABELEDLIST LabeledProtoList; + + LabeledProtoList = NewLabeledList(CharName); + iterate(ProtoList) + { + Proto = (PROTOTYPE *) first (ProtoList); + LabeledProtoList->List = push(LabeledProtoList->List, Proto); + } + *NormProtoList = push(*NormProtoList, LabeledProtoList); +} + +/*-------------------------------------------------------------------------*/ +void WriteProtos( + FILE *File, + UINT16 N, + LIST ProtoList, + BOOL8 WriteSigProtos, + BOOL8 WriteInsigProtos) +{ + PROTOTYPE *Proto; + + // write prototypes + iterate(ProtoList) + { + Proto = (PROTOTYPE *) first ( ProtoList ); + if (( Proto->Significant && WriteSigProtos ) || + ( ! Proto->Significant && WriteInsigProtos ) ) + WritePrototype( File, N, Proto ); + } +} // WriteProtos + +/*---------------------------------------------------------------------------*/ +int NumberOfProtos( + LIST ProtoList, + BOOL8 CountSigProtos, + BOOL8 CountInsigProtos) +{ + int N = 0; + PROTOTYPE *Proto; + + iterate(ProtoList) + { + Proto = (PROTOTYPE *) first ( ProtoList ); + if (( Proto->Significant && CountSigProtos ) || + ( ! Proto->Significant && CountInsigProtos ) ) + N++; + } + return(N); +} + diff --git a/training/cnTraining.dsp b/training/cnTraining.dsp new file mode 100644 index 0000000000..b067426e28 --- /dev/null +++ b/training/cnTraining.dsp @@ -0,0 +1,227 @@ +# Microsoft Developer Studio Project File - Name="cnTraining" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=cnTraining - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "cnTraining.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "cnTraining.mak" CFG="cnTraining - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "cnTraining - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "cnTraining - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "cnTraining - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\release\" +# PROP Intermediate_Dir "..\..\release\train2" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../ccutil" /I "../ccstruct" /I "../classify" /I "../cutil" /I "../training" /I "../viewer" /I "../dict" /D "TRAINING" /D "WIN32" /D "_WINDOWS" /D "__NT__" /D "__MSW32__" /D "_AFXDLL" /Fr /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 /nologo /subsystem:console /profile /debug /machine:I386 + +!ELSEIF "$(CFG)" == "cnTraining - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\debug" +# PROP Intermediate_Dir "..\..\debug\train2" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../ccutil" /I "../ccstruct" /I "../classify" /I "../cutil" /I "../training" /I "../viewer" /I "../dict" /D "_DEBUG" /D "TRAINING" /D "WIN32" /D "_WINDOWS" /D "__NT__" /D "__MSW32__" /D "_AFXDLL" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "cnTraining - Win32 Release" +# Name "cnTraining - Win32 Debug" +# Begin Source File + +SOURCE=..\ccutil\clst.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\cluster.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\clusttool.cpp +# End Source File +# Begin Source File + +SOURCE=.\cnTraining.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\cutil.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\danerror.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\debug.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\debugwin.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\efio.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\emalloc.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\errcode.cpp +# End Source File +# Begin Source File + +SOURCE=..\viewer\evntlst.cpp +# End Source File +# Begin Source File + +SOURCE=..\viewer\evnts.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\featdefs.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\freelist.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\getopt.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\globaloc.cpp +# End Source File +# Begin Source File + +SOURCE=..\viewer\grphics.cpp +# End Source File +# Begin Source File + +SOURCE=..\viewer\grphshm.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\hashfn.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\kdtree.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\listio.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\memblk.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\memry.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\mfcpch.cpp +# End Source File +# Begin Source File + +SOURCE=..\training\name2char.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\ocrfeatures.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\oldheap.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\oldlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\strngs.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\structures.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\tprintf.cpp +# End Source File +# Begin Source File + +SOURCE=..\training\training.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\varable.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\variables.cpp +# End Source File +# End Target +# End Project diff --git a/training/cnTraining1.dsp b/training/cnTraining1.dsp new file mode 100755 index 0000000000..00fee9146f --- /dev/null +++ b/training/cnTraining1.dsp @@ -0,0 +1,85 @@ +# Microsoft Developer Studio Project File - Name="cnTraining1" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=cnTraining1 - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "cnTraining1.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "cnTraining1.mak" CFG="cnTraining1 - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "cnTraining1 - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "cnTraining1 - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "cnTraining1 - Win32 Release" + +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f cnTraining.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "cnTraining.exe" +# PROP BASE Bsc_Name "cnTraining.bsc" +# PROP BASE Target_Dir "" +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Cmd_Line "NMAKE /f cnTraining.mak" +# PROP Rebuild_Opt "/a" +# PROP Target_File "cnTraining1.exe" +# PROP Bsc_Name "cnTraining1.bsc" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "cnTraining1 - Win32 Debug" + +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Cmd_Line "NMAKE /f cnTraining.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "cnTraining.exe" +# PROP BASE Bsc_Name "cnTraining.bsc" +# PROP BASE Target_Dir "" +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Cmd_Line "NMAKE /f cnTraining.mak" +# PROP Rebuild_Opt "/a" +# PROP Target_File "cnTraining1.exe" +# PROP Bsc_Name "cnTraining1.bsc" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "cnTraining1 - Win32 Release" +# Name "cnTraining1 - Win32 Debug" + +!IF "$(CFG)" == "cnTraining1 - Win32 Release" + +!ELSEIF "$(CFG)" == "cnTraining1 - Win32 Debug" + +!ENDIF + +# Begin Source File + +SOURCE=.\cnTraining.dsp +# End Source File +# End Target +# End Project diff --git a/training/mergenf.cpp b/training/mergenf.cpp new file mode 100644 index 0000000000..a32d260cf8 --- /dev/null +++ b/training/mergenf.cpp @@ -0,0 +1,451 @@ +/****************************************************************************** +** Filename: MergeNF.c +** Purpose: Program for merging similar nano-feature protos +** Author: Dan Johnson +** History: Wed Nov 21 09:55:23 1990, DSJ, Created. +** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. +******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "mergenf.h" +#include "general.h" +#include "efio.h" +#include "clusttool.h" +#include "cluster.h" +#include "oldlist.h" +#include "protos.h" +#include "minmax.h" +#include "ocrfeatures.h" +#include "debug.h" +#include "const.h" +#include "featdefs.h" +#include "intproto.h" + +#include +#include +#include + + +/**---------------------------------------------------------------------------- + Variables +-----------------------------------------------------------------------------**/ +/*-------------------once in subfeat---------------------------------*/ +make_float_var (AngleMatchScale, 1.0, MakeAngleMatchScale, + 7, 2, SetAngleMatchScale, "Angle Match Scale ...") + +make_float_var (SimilarityMidpoint, 0.0075, MakeSimilarityMidpoint, + 7, 3, SetSimilarityMidpoint, "Similarity Midpoint ...") + +make_float_var (SimilarityCurl, 2.0, MakeSimilarityCurl, + 7, 4, SetSimilarityCurl, "Similarity Curl ...") + +/*-----------------------------once in fasttrain----------------------------------*/ +make_float_var (TangentBBoxPad, 0.5, MakeTangentBBoxPad, + 15, 3, SetTangentBBoxPad, "Tangent bounding box pad ...") + +make_float_var (OrthogonalBBoxPad, 2.5, MakeOrthogonalBBoxPad, + 15, 4, SetOrthogonalBBoxPad, "Orthogonal bounding box pad ...") + +make_float_var (AnglePad, 45.0, MakeAnglePad, + 15, 5, SetAnglePad, "Angle pad ...") + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +//int row_number; /* kludge due to linking problems */ + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FLOAT32 CompareProtos ( + PROTO p1, + PROTO p2) + +/* +** Parameters: +** p1, p2 protos to be compared +** Globals: none +** Operation: Compare protos p1 and p2 and return an estimate of the +** worst evidence rating that will result for any part of p1 +** that is compared to p2. In other words, if p1 were broken +** into pico-features and each pico-feature was matched to p2, +** what is the worst evidence rating that will be achieved for +** any pico-feature. +** Return: Worst possible result when matching p1 to p2. +** Exceptions: none +** History: Mon Nov 26 08:27:53 1990, DSJ, Created. +*/ + +{ + FEATURE Feature; + FLOAT32 WorstEvidence = WORST_EVIDENCE; + FLOAT32 Evidence; + FLOAT32 Angle, Length; + + /* if p1 and p2 are not close in length, don't let them match */ + Length = fabs (ProtoLength (p1) - ProtoLength (p2)); + if (Length > MAX_LENGTH_MISMATCH) + return (0.0); + + /* create a dummy pico-feature to be used for comparisons */ + Feature = NewFeature (&PicoFeatDesc); + ParamOf (Feature, PicoFeatDir) = ProtoAngle (p1); + + /* convert angle to radians */ + Angle = ProtoAngle (p1) * 2.0 * PI; + + /* find distance from center of p1 to 1/2 picofeat from end */ + Length = ProtoLength (p1) / 2.0 - GetPicoFeatureLength () / 2.0; + if (Length < 0) Length = 0; + + /* set the dummy pico-feature at one end of p1 and match it to p2 */ + ParamOf (Feature, PicoFeatX) = ProtoX (p1) + cos (Angle) * Length; + ParamOf (Feature, PicoFeatY) = ProtoY (p1) + sin (Angle) * Length; + if (DummyFastMatch (Feature, p2)) + { + Evidence = SubfeatureEvidence (Feature, p2); + if (Evidence < WorstEvidence) + WorstEvidence = Evidence; + } + else + { + FreeFeature (Feature); + return (0.0); + } + + /* set the dummy pico-feature at the other end of p1 and match it to p2 */ + ParamOf (Feature, PicoFeatX) = ProtoX (p1) - cos (Angle) * Length; + ParamOf (Feature, PicoFeatY) = ProtoY (p1) - sin (Angle) * Length; + if (DummyFastMatch (Feature, p2)) + { + Evidence = SubfeatureEvidence (Feature, p2); + if (Evidence < WorstEvidence) + WorstEvidence = Evidence; + } + else + { + FreeFeature (Feature); + return (0.0); + } + + FreeFeature (Feature); + return (WorstEvidence); + +} /* CompareProtos */ + +/*---------------------------------------------------------------------------*/ +void ComputeMergedProto ( + PROTO p1, + PROTO p2, + FLOAT32 w1, + FLOAT32 w2, + PROTO MergedProto) + +/* +** Parameters: +** p1, p2 protos to be merged +** w1, w2 weight of each proto +** MergedProto place to put resulting merged proto +** Globals: none +** Operation: This routine computes a proto which is the weighted +** average of protos p1 and p2. The new proto is returned +** in MergedProto. +** Return: none (results are returned in MergedProto) +** Exceptions: none +** History: Mon Nov 26 08:15:08 1990, DSJ, Created. +*/ + +{ + FLOAT32 TotalWeight; + + TotalWeight = w1 + w2; + w1 /= TotalWeight; + w2 /= TotalWeight; + + ProtoX (MergedProto) = ProtoX (p1) * w1 + ProtoX (p2) * w2; + ProtoY (MergedProto) = ProtoY (p1) * w1 + ProtoY (p2) * w2; + ProtoLength (MergedProto) = ProtoLength (p1) * w1 + ProtoLength (p2) * w2; + ProtoAngle (MergedProto) = ProtoAngle (p1) * w1 + ProtoAngle (p2) * w2; + FillABC (MergedProto); + +} /* ComputeMergedProto */ + +/*---------------------------------------------------------------------------*/ +int FindClosestExistingProto ( + CLASS_TYPE Class, + int NumMerged[], + PROTOTYPE *Prototype) + +/* +** Parameters: +** Class class to search for matching old proto in +** NumMerged[] # of protos merged into each proto of Class +** Prototype new proto to find match for +** Globals: none +** Operation: This routine searches thru all of the prototypes in +** Class and returns the id of the proto which would provide +** the best approximation of Prototype. If no close +** approximation can be found, NO_PROTO is returned. +** Return: Id of closest proto in Class or NO_PROTO. +** Exceptions: none +** History: Sat Nov 24 11:42:58 1990, DSJ, Created. +*/ + +{ + PROTO_STRUCT NewProto; + PROTO_STRUCT MergedProto; + int Pid; + PROTO Proto; + int BestProto; + FLOAT32 BestMatch; + FLOAT32 Match, OldMatch, NewMatch; + + MakeNewFromOld (&NewProto, Prototype); + + BestProto = NO_PROTO; + BestMatch = WORST_MATCH_ALLOWED; + for (Pid = 0; Pid < NumProtosIn (Class); Pid++) + { + Proto = ProtoIn (Class, Pid); + ComputeMergedProto (Proto, &NewProto, + (FLOAT32) NumMerged[Pid], 1.0, &MergedProto); + OldMatch = CompareProtos (Proto, &MergedProto); + NewMatch = CompareProtos (&NewProto, &MergedProto); + Match = MIN (OldMatch, NewMatch); + if (Match > BestMatch) + { + BestProto = Pid; + BestMatch = Match; + } + } + return (BestProto); + +} /* FindClosestExistingProto */ + +/*---------------------------------------------------------------------------*/ +void MakeNewFromOld ( + PROTO New, + PROTOTYPE *Old) + +/* +** Parameters: +** New new proto to be filled in +** Old old proto to be converted +** Globals: none +** Operation: This fills in the fields of the New proto based on the +** fields of the Old proto. +** Return: none +** Exceptions: none +** History: Mon Nov 26 09:45:39 1990, DSJ, Created. +*/ + +{ + ProtoX (New) = CenterX (Old->Mean); + ProtoY (New) = CenterY (Old->Mean); + ProtoLength (New) = LengthOf (Old->Mean); + ProtoAngle (New) = OrientationOf (Old->Mean); + FillABC (New); + +} /* MakeNewFromOld */ + +/*-------------------once in subfeat---------------------------------*/ +/********************************************************************** +* InitSubfeatureVars +* +* Create and set up all menus and variables needed for this file. +**********************************************************************/ +void InitSubfeatureVars () +{ + MakeAngleMatchScale (); + MakeSimilarityCurl (); + MakeSimilarityMidpoint (); +} + + +/********************************************************************** +* SubfeatureEvidence +* +* Compare a feature to a prototype. Print the result. +**********************************************************************/ +FLOAT32 SubfeatureEvidence ( + FEATURE Feature, + PROTO Proto) +{ + float Distance; + float Dangle; + + Dangle = ProtoAngle (Proto) - ParamOf(Feature, PicoFeatDir); + if (Dangle < -0.5) Dangle += 1.0; + if (Dangle > 0.5) Dangle -= 1.0; + Dangle *= AngleMatchScale; + + Distance = CoefficientA (Proto) * ParamOf(Feature, PicoFeatX) + + CoefficientB (Proto) * ParamOf(Feature, PicoFeatY) + + CoefficientC (Proto); + + return (EvidenceOf (Distance * Distance + Dangle * Dangle)); +} + +/********************************************************************** +* EvidenceOf +* +* Return the new type of evidence number corresponding to this +* distance value. This number is no longer based on the chi squared +* approximation. The equation that represents the transform is: +* 1 / (1 + (sim / midpoint) ^ curl) +**********************************************************************/ +FLOAT32 EvidenceOf ( + register FLOAT32 Similarity) +{ + + Similarity /= SimilarityMidpoint; + + if (SimilarityCurl == 3) + Similarity = Similarity * Similarity * Similarity; + else if (SimilarityCurl == 2) + Similarity = Similarity * Similarity; + else + Similarity = pow (Similarity, SimilarityCurl); + + return (1.0 / (1.0 + Similarity)); +} + +/*-----------------------------once in fasttrain----------------------------------*/ +void InitFastTrainerVars () +/* +** Parameters: none +** Globals: none +** Operation: This routine initializes all of the control variables +** for the fast trainer. +** Return: none +** Exceptions: none +** History: Mon Nov 12 13:27:35 1990, DSJ, Created. +*/ + +{ + MakeTangentBBoxPad (); + MakeOrthogonalBBoxPad (); + MakeAnglePad (); + +} /* InitFastTrainerVars */ + +/*---------------------------------------------------------------------------*/ +BOOL8 DummyFastMatch ( + FEATURE Feature, + PROTO Proto) + +/* +** Parameters: +** Feature feature to be "fast matched" to proto +** Proto proto being "fast matched" against +** Globals: +** TangentBBoxPad bounding box pad tangent to proto +** OrthogonalBBoxPad bounding box pad orthogonal to proto +** Operation: This routine returns TRUE if Feature would be matched +** by a fast match table built from Proto. +** Return: TRUE if feature could match Proto. +** Exceptions: none +** History: Wed Nov 14 17:19:58 1990, DSJ, Created. +*/ + +{ + FRECT BoundingBox; + FLOAT32 MaxAngleError; + FLOAT32 AngleError; + + MaxAngleError = AnglePad / 360.0; + AngleError = fabs (ProtoAngle (Proto) - ParamOf (Feature, PicoFeatDir)); + if (AngleError > 0.5) + AngleError = 1.0 - AngleError; + + if (AngleError > MaxAngleError) + return (FALSE); + + ComputePaddedBoundingBox (Proto, + TangentBBoxPad * GetPicoFeatureLength (), + OrthogonalBBoxPad * GetPicoFeatureLength (), + &BoundingBox); + + return (PointInside (&BoundingBox, + ParamOf (Feature, PicoFeatX), + ParamOf (Feature, PicoFeatY))); + +} /* DummyFastMatch */ + +/*----------------------------------------------------------------------------*/ +void ComputePaddedBoundingBox ( + PROTO Proto, + FLOAT32 TangentPad, + FLOAT32 OrthogonalPad, + FRECT *BoundingBox) + +/* +** Parameters: +** Proto proto to compute bounding box for +** TangentPad amount of pad to add in direction of segment +** OrthogonalPad amount of pad to add orthogonal to segment +** BoundingBox place to put results +** Globals: none +** Operation: This routine computes a bounding box that encloses the +** specified proto along with some padding. The +** amount of padding is specified as separate distances +** in the tangential and orthogonal directions. +** Return: none (results are returned in BoundingBox) +** Exceptions: none +** History: Wed Nov 14 14:55:30 1990, DSJ, Created. +*/ + +{ + FLOAT32 Pad, Length, Angle; + FLOAT32 CosOfAngle, SinOfAngle; + + Length = ProtoLength (Proto) / 2.0 + TangentPad; + Angle = ProtoAngle (Proto) * 2.0 * PI; + CosOfAngle = fabs (cos (Angle)); + SinOfAngle = fabs (sin (Angle)); + + Pad = MAX (CosOfAngle * Length, SinOfAngle * OrthogonalPad); + BoundingBox->MinX = ProtoX (Proto) - Pad; + BoundingBox->MaxX = ProtoX (Proto) + Pad; + + Pad = MAX (SinOfAngle * Length, CosOfAngle * OrthogonalPad); + BoundingBox->MinY = ProtoY (Proto) - Pad; + BoundingBox->MaxY = ProtoY (Proto) + Pad; + +} /* ComputePaddedBoundingBox */ + +/*--------------------------------------------------------------------------*/ +BOOL8 PointInside ( + FRECT *Rectangle, + FLOAT32 X, + FLOAT32 Y) + +/* +** Parameters: +** Globals: none +** Operation: Return TRUE if point (X,Y) is inside of Rectangle. +** Return: Return TRUE if point (X,Y) is inside of Rectangle. +** Exceptions: none +** History: Wed Nov 14 17:26:35 1990, DSJ, Created. +*/ + +{ + if (X < Rectangle->MinX) return (FALSE); + if (X > Rectangle->MaxX) return (FALSE); + if (Y < Rectangle->MinY) return (FALSE); + if (Y > Rectangle->MaxY) return (FALSE); + return (TRUE); + +} /* PointInside */ diff --git a/training/mergenf.h b/training/mergenf.h new file mode 100644 index 0000000000..af62d3021c --- /dev/null +++ b/training/mergenf.h @@ -0,0 +1,106 @@ +/****************************************************************************** +** Filename: MergeNF.c +** Purpose: Program for merging similar nano-feature protos +** Author: Dan Johnson +** History: Wed Nov 21 09:55:23 1990, DSJ, Created. +** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. +******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "protos.h" +#include "cluster.h" +#include "ocrfeatures.h" +#include "training.h" + + +#define WORST_MATCH_ALLOWED (0.9) +#define WORST_EVIDENCE (1.0) +#define MAX_LENGTH_MISMATCH (2.0 * GetPicoFeatureLength ()) + + +#define PROTO_SUFFIX ".mf.p" +#define CONFIG_SUFFIX ".cl" +#define NO_PROTO (-1) +#define XPOSITION 0 +#define YPOSITION 1 +#define MFLENGTH 2 +#define ORIENTATION 3 + +typedef enum {PicoFeatY, PicoFeatDir, PicoFeatX} PICO_FEAT_PARAM_NAME; + +typedef struct +{ + FLOAT32 MinX, MaxX, MinY, MaxY; +} FRECT; + +/**---------------------------------------------------------------------------- + Public Macros +----------------------------------------------------------------------------**/ +#define CenterX(M) ( (M)[XPOSITION] ) +#define CenterY(M) ( (M)[YPOSITION] ) +#define LengthOf(M) ( (M)[MFLENGTH] ) +#define OrientationOf(M) ( (M)[ORIENTATION] ) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +FLOAT32 CompareProtos ( + PROTO p1, + PROTO p2); + +void ComputeMergedProto ( + PROTO p1, + PROTO p2, + FLOAT32 w1, + FLOAT32 w2, + PROTO MergedProto); + +int FindClosestExistingProto ( + CLASS_TYPE Class, + int NumMerged[], + PROTOTYPE *Prototype); + +void MakeNewFromOld ( + PROTO New, + PROTOTYPE *Old); + +void InitSubfeatureVars (); + +FLOAT32 SubfeatureEvidence ( + FEATURE Feature, + PROTO Proto); + +FLOAT32 EvidenceOf ( + register FLOAT32 Similarity); + +void InitFastTrainerVars (); + +BOOL8 DummyFastMatch ( + FEATURE Feature, + PROTO Proto); + +void ComputePaddedBoundingBox ( + PROTO Proto, + FLOAT32 TangentPad, + FLOAT32 OrthogonalPad, + FRECT *BoundingBox); + +BOOL8 PointInside ( + FRECT *Rectangle, + FLOAT32 X, + FLOAT32 Y); + +extern FEATURE_DESC_STRUCT PicoFeatDesc; + + diff --git a/training/mfTraining.cpp b/training/mfTraining.cpp new file mode 100644 index 0000000000..9f9c6affdd --- /dev/null +++ b/training/mfTraining.cpp @@ -0,0 +1,1206 @@ +/****************************************************************************** +** Filename: mfTraining.c +** Purpose: Separates training pages into files for each character. +** Strips from files only the features and there parameters of + the feature type mf. +** Author: Dan Johnson +** Revisment: Christy Russon +** Environment: HPUX 6.5 +** Library: HPUX 6.5 +** History: Fri Aug 18 08:53:50 1989, DSJ, Created. +** 5/25/90, DSJ, Adapted to multiple feature types. +** Tuesday, May 17, 1998 Changes made to make feature specific and +** simplify structures. First step in simplifying training process. +** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. +******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "oldlist.h" +#include "efio.h" +#include "emalloc.h" +#include "featdefs.h" +#include "getopt.h" +#include "ocrfeatures.h" +#include "general.h" +#include "clusttool.h" +#include "cluster.h" +#include "protos.h" +#include "minmax.h" +#include "debug.h" +#include "const.h" +#include "mergenf.h" +#include "name2char.h" +#include "intproto.h" +#include "variables.h" +#include "freelist.h" + +#include +#include +#include + +#define MAXNAMESIZE 80 +#define MAX_NUM_SAMPLES 10000 +#define PROGRAM_FEATURE_TYPE "mf" +#define MINSD (1.0f / 128.0f) + +int row_number; /* cjn: fixes link problem */ + +typedef struct +{ + char *Label; + LIST List; +} +LABELEDLISTNODE, *LABELEDLIST; + +typedef struct +{ + char* Label; + int NumMerged[MAX_NUM_PROTOS]; + CLASS_TYPE Class; +}MERGE_CLASS_NODE; +typedef MERGE_CLASS_NODE* MERGE_CLASS; + +#define round(x,frag)(floor(x/frag+.5)*frag) + + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +int main ( + int argc, + char **argv); + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +void ParseArguments( +int argc, +char **argv); + +char *GetNextFilename (); + +LIST ReadTrainingSamples ( + FILE *File); + +LABELEDLIST FindList ( + LIST List, + char *Label); + +MERGE_CLASS FindClass ( + LIST List, + char *Label); + +LABELEDLIST NewLabeledList ( + char *Label); + +MERGE_CLASS NewLabeledClass ( + char *Label); + +void WriteTrainingSamples ( + char *Directory, + LIST CharList); + +void WriteClusteredTrainingSamples ( + char *Directory, + LIST ProtoList, + CLUSTERER *Clusterer, + LABELEDLIST CharSample); +/**/ +void WriteMergedTrainingSamples( + char *Directory, + LIST ClassList); + +void WriteMicrofeat( + char *Directory, + LIST ClassList); + +void WriteProtos( + FILE* File, + MERGE_CLASS MergeClass); + +void WriteConfigs( + FILE* File, + CLASS_TYPE Class); + +void FreeTrainingSamples ( + LIST CharList); + +void FreeLabeledClassList ( + LIST ClassList); + +void FreeLabeledList ( + LABELEDLIST LabeledList); + +CLUSTERER *SetUpForClustering( + LABELEDLIST CharSample); +/* +PARAMDESC *ConvertToPARAMDESC( + PARAM_DESC* Param_Desc, + int N); +*/ +LIST RemoveInsignificantProtos( + LIST ProtoList, + BOOL8 KeepSigProtos, + BOOL8 KeepInsigProtos, + int N); + +void CleanUpUnusedData( + LIST ProtoList); + +void Normalize ( + float *Values); + +void SetUpForFloat2Int( + LIST LabeledClassList); + +void WritePFFMTable(INT_TEMPLATES Templates, const char* filename) { + FILE* fp = Efopen(filename, "wb"); + /* then write out each class */ + for (int i = 0; i < NumClassesIn (Templates); i++) { + int MaxLength = 0; + INT_CLASS Class = ClassForIndex (Templates, i); + for (int ConfigId = 0; ConfigId < NumIntConfigsIn (Class); ConfigId++) { + if (LengthForConfigId (Class, ConfigId) > MaxLength) + MaxLength = LengthForConfigId (Class, ConfigId); + } + fprintf(fp, "%c %d\n", ClassIdForIndex(Templates, i), MaxLength); + } + fclose(fp); +} + + +//--------------Global Data Definitions and Declarations-------------- +static char FontName[MAXNAMESIZE]; +// globals used for parsing command line arguments +static char *Directory = NULL; +static int MaxNumSamples = MAX_NUM_SAMPLES; +static int Argc; +static char **Argv; + +// globals used to control what information is saved in the output file +static BOOL8 ShowAllSamples = FALSE; +static BOOL8 ShowSignificantProtos = TRUE; +static BOOL8 ShowInsignificantProtos = FALSE; + +// global variable to hold configuration parameters to control clustering +// -M 0.40 -B 0.05 -I 1.0 -C 1e-6. +static CLUSTERCONFIG Config = +{ elliptical, 0.40, 0.05, 1.0, 1e-6 }; + +static FLOAT32 RoundingAccuracy = 0.0; + +/*---------------------------------------------------------------------------- + Public Code +-----------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +int main ( + int argc, + char **argv) + +/* +** Parameters: +** argc number of command line arguments +** argv array of command line arguments +** Globals: none +** Operation: +** This program reads in a text file consisting of feature +** samples from a training page in the following format: +** +** FontName CharName NumberOfFeatureTypes(N) +** FeatureTypeName1 NumberOfFeatures(M) +** Feature1 +** ... +** FeatureM +** FeatureTypeName2 NumberOfFeatures(M) +** Feature1 +** ... +** FeatureM +** ... +** FeatureTypeNameN NumberOfFeatures(M) +** Feature1 +** ... +** FeatureM +** FontName CharName ... +** +** The result of this program is a binary inttemp file used by +** the OCR engine. +** Return: none +** Exceptions: none +** History: Fri Aug 18 08:56:17 1989, DSJ, Created. +** Mon May 18 1998, Christy Russson, Revistion started. +*/ + +{ + char *PageName; + FILE *TrainingPage; + FILE *OutFile; + LIST CharList; + CLUSTERER *Clusterer = NULL; + LIST ProtoList = NIL; + LABELEDLIST CharSample; + PROTOTYPE *Prototype; + LIST ClassList = NIL; + int Cid, Pid; + PROTO Proto; + PROTO_STRUCT DummyProto; + BIT_VECTOR Config2; + MERGE_CLASS MergeClass; + INT_TEMPLATES IntTemplates; + LIST pCharList, pProtoList; + char Filename[MAXNAMESIZE]; + + ParseArguments (argc, argv); + InitFastTrainerVars (); + InitSubfeatureVars (); + while ((PageName = GetNextFilename()) != NULL) + { + printf ("\nReading %s ...", PageName); + TrainingPage = Efopen (PageName, "r"); + CharList = ReadTrainingSamples (TrainingPage); + fclose (TrainingPage); + //WriteTrainingSamples (Directory, CharList); + pCharList = CharList; + iterate(pCharList) + { + //Cluster + CharSample = (LABELEDLIST) first (pCharList); + printf ("\nClustering %s ...", CharSample->Label); + Clusterer = SetUpForClustering(CharSample); + ProtoList = ClusterSamples(Clusterer, &Config); + //WriteClusteredTrainingSamples (Directory, ProtoList, Clusterer, CharSample); + CleanUpUnusedData(ProtoList); + + //Merge + ProtoList = RemoveInsignificantProtos(ProtoList, ShowSignificantProtos, + ShowInsignificantProtos, Clusterer->SampleSize); + FreeClusterer(Clusterer); + MergeClass = FindClass (ClassList, CharSample->Label); + if (MergeClass == NULL) + { + MergeClass = NewLabeledClass (CharSample->Label); + ClassList = push (ClassList, MergeClass); + } + Cid = AddConfigToClass(MergeClass->Class); + pProtoList = ProtoList; + iterate (pProtoList) + { + Prototype = (PROTOTYPE *) first (pProtoList); + + // see if proto can be approximated by existing proto + Pid = FindClosestExistingProto (MergeClass->Class, MergeClass->NumMerged, Prototype); + if (Pid == NO_PROTO) + { + Pid = AddProtoToClass (MergeClass->Class); + Proto = ProtoIn (MergeClass->Class, Pid); + MakeNewFromOld (Proto, Prototype); + MergeClass->NumMerged[Pid] = 1; + } + else + { + MakeNewFromOld (&DummyProto, Prototype); + ComputeMergedProto (ProtoIn (MergeClass->Class, Pid), &DummyProto, + (FLOAT32) MergeClass->NumMerged[Pid], 1.0, + ProtoIn (MergeClass->Class, Pid)); + MergeClass->NumMerged[Pid] ++; + } + Config2 = ConfigIn (MergeClass->Class, Cid); + AddProtoToConfig (Pid, Config2); + } + FreeProtoList (&ProtoList); + } + FreeTrainingSamples (CharList); + printf ("\n"); + } + //WriteMergedTrainingSamples(Directory,ClassList); + WriteMicrofeat(Directory, ClassList); + InitIntProtoVars (); + InitPrototypes (); + SetUpForFloat2Int(ClassList); + IntTemplates = CreateIntTemplates(TrainingData); + strcpy (Filename, ""); + if (Directory != NULL) + { + strcat (Filename, Directory); + strcat (Filename, "/"); + } + strcat (Filename, "inttemp"); +#ifdef __UNIX__ + OutFile = Efopen (Filename, "w"); +#else + OutFile = Efopen (Filename, "wb"); +#endif + WriteIntTemplates(OutFile, IntTemplates); + fclose (OutFile); + // Now create pffmtable. + WritePFFMTable(IntTemplates, "pffmtable"); + printf ("\nDone!\n"); /**/ + FreeLabeledClassList (ClassList); + return 0; +} /* main */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ParseArguments( +int argc, +char **argv) + +/* +** Parameters: +** argc number of command line arguments to parse +** argv command line arguments +** Globals: +** ShowAllSamples flag controlling samples display +** ShowSignificantProtos flag controlling proto display +** ShowInsignificantProtos flag controlling proto display +** Config current clustering parameters +** optarg, optind defined by getopt sys call +** Argc, Argv global copies of argc and argv +** Operation: +** This routine parses the command line arguments that were +** passed to the program. The legal arguments are: +** -d "turn off display of samples" +** -p "turn off significant protos" +** -n "turn off insignificant proto" +** -S [ spherical | elliptical | mixed | automatic ] +** -M MinSamples "min samples per prototype (%)" +** -B MaxIllegal "max illegal chars per cluster (%)" +** -I Independence "0 to 1" +** -C Confidence "1e-200 to 1.0" +** -D Directory +** -N MaxNumSamples +** -R RoundingAccuracy +** Return: none +** Exceptions: Illegal options terminate the program. +** History: 7/24/89, DSJ, Created. +*/ + +{ + int Option; + int ParametersRead; + BOOL8 Error; + extern char *optarg; + + Error = FALSE; + Argc = argc; + Argv = argv; + while (( Option = getopt( argc, argv, "R:N:D:C:I:M:B:S:d:n:p" )) != EOF ) + { + switch ( Option ) + { + case 'n': + ShowInsignificantProtos = FALSE; + break; + case 'p': + ShowSignificantProtos = FALSE; + break; + case 'd': + ShowAllSamples = FALSE; + break; + case 'C': + ParametersRead = sscanf( optarg, "%lf", &(Config.Confidence) ); + if ( ParametersRead != 1 ) Error = TRUE; + else if ( Config.Confidence > 1 ) Config.Confidence = 1; + else if ( Config.Confidence < 0 ) Config.Confidence = 0; + break; + case 'I': + ParametersRead = sscanf( optarg, "%f", &(Config.Independence) ); + if ( ParametersRead != 1 ) Error = TRUE; + else if ( Config.Independence > 1 ) Config.Independence = 1; + else if ( Config.Independence < 0 ) Config.Independence = 0; + break; + case 'M': + ParametersRead = sscanf( optarg, "%f", &(Config.MinSamples) ); + if ( ParametersRead != 1 ) Error = TRUE; + else if ( Config.MinSamples > 1 ) Config.MinSamples = 1; + else if ( Config.MinSamples < 0 ) Config.MinSamples = 0; + break; + case 'B': + ParametersRead = sscanf( optarg, "%f", &(Config.MaxIllegal) ); + if ( ParametersRead != 1 ) Error = TRUE; + else if ( Config.MaxIllegal > 1 ) Config.MaxIllegal = 1; + else if ( Config.MaxIllegal < 0 ) Config.MaxIllegal = 0; + break; + case 'R': + ParametersRead = sscanf( optarg, "%f", &RoundingAccuracy ); + if ( ParametersRead != 1 ) Error = TRUE; + else if ( RoundingAccuracy > 0.01 ) RoundingAccuracy = 0.01; + else if ( RoundingAccuracy < 0.0 ) RoundingAccuracy = 0.0; + break; + case 'S': + switch ( optarg[0] ) + { + case 's': Config.ProtoStyle = spherical; break; + case 'e': Config.ProtoStyle = elliptical; break; + case 'm': Config.ProtoStyle = mixed; break; + case 'a': Config.ProtoStyle = automatic; break; + default: Error = TRUE; + } + break; + case 'D': + Directory = optarg; + break; + case 'N': + if (sscanf (optarg, "%d", &MaxNumSamples) != 1 || + MaxNumSamples <= 0) + Error = TRUE; + break; + case '?': + Error = TRUE; + break; + } + if ( Error ) + { + fprintf (stderr, "usage: %s [-D] [-P] [-N]\n", argv[0] ); + fprintf (stderr, "\t[-S ProtoStyle]\n"); + fprintf (stderr, "\t[-M MinSamples] [-B MaxBad] [-I Independence] [-C Confidence]\n" ); + fprintf (stderr, "\t[-d directory] [-n MaxNumSamples] [ TrainingPage ... ]\n"); + exit (2); + } + } +} // ParseArguments + +/*---------------------------------------------------------------------------*/ +char *GetNextFilename () +/* +** Parameters: none +** Globals: +** optind defined by getopt sys call +** Argc, Argv global copies of argc and argv +** Operation: +** This routine returns the next command line argument. If +** there are no remaining command line arguments, it returns +** NULL. This routine should only be called after all option +** arguments have been parsed and removed with ParseArguments. +** Return: Next command line argument or NULL. +** Exceptions: none +** History: Fri Aug 18 09:34:12 1989, DSJ, Created. +*/ + +{ + if (optind < Argc) + return (Argv [optind++]); + else + return (NULL); + +} /* GetNextFilename */ + +/*---------------------------------------------------------------------------*/ +LIST ReadTrainingSamples ( + FILE *File) + +/* +** Parameters: +** File open text file to read samples from +** Globals: none +** Operation: +** This routine reads training samples from a file and +** places them into a data structure which organizes the +** samples by FontName and CharName. It then returns this +** data structure. +** Return: none +** Exceptions: none +** History: Fri Aug 18 13:11:39 1989, DSJ, Created. +** Tue May 17 1998 simplifications to structure, illiminated +** font, and feature specification levels of structure. +*/ + +{ + char CharName[MAXNAMESIZE]; + LABELEDLIST CharSample; + FEATURE_SET FeatureSamples; + LIST TrainingSamples = NIL; + CHAR_DESC CharDesc; + int Type, i; + + while (fscanf (File, "%s %s", FontName, CharName) == 2) { + CharSample = FindList (TrainingSamples, CharName); + if (CharSample == NULL) { + CharSample = NewLabeledList (CharName); + TrainingSamples = push (TrainingSamples, CharSample); + } + CharDesc = ReadCharDescription (File); + Type = ShortNameToFeatureType(PROGRAM_FEATURE_TYPE); + FeatureSamples = FeaturesOfType(CharDesc, Type); + for (int feature = 0; feature < FeatureSamples->NumFeatures; ++feature) { + FEATURE f = FeatureSamples->Features[feature]; + for (int dim =0; dim < f->Type->NumParams; ++dim) + f->Params[dim] += UniformRandomNumber(-MINSD, MINSD); + } + CharSample->List = push (CharSample->List, FeatureSamples); + for (i = 0; i < NumFeatureSetsIn (CharDesc); i++) + if (Type != i) + FreeFeatureSet (FeaturesOfType (CharDesc, i)); + free (CharDesc); + } + return (TrainingSamples); + +} /* ReadTrainingSamples */ + +/*---------------------------------------------------------------------------*/ +LABELEDLIST FindList ( + LIST List, + char *Label) + +/* +** Parameters: +** List list to search +** Label label to search for +** Globals: none +** Operation: +** This routine searches thru a list of labeled lists to find +** a list with the specified label. If a matching labeled list +** cannot be found, NULL is returned. +** Return: Labeled list with the specified Label or NULL. +** Exceptions: none +** History: Fri Aug 18 15:57:41 1989, DSJ, Created. +*/ + +{ + LABELEDLIST LabeledList; + + iterate (List) + { + LabeledList = (LABELEDLIST) first (List); + if (strcmp (LabeledList->Label, Label) == 0) + return (LabeledList); + } + return (NULL); + +} /* FindList */ + +/*----------------------------------------------------------------------------*/ +MERGE_CLASS FindClass ( + LIST List, + char *Label) +{ + MERGE_CLASS MergeClass; + + iterate (List) + { + MergeClass = (MERGE_CLASS) first (List); + if (strcmp (MergeClass->Label, Label) == 0) + return (MergeClass); + } + return (NULL); + +} /* FindClass */ + +/*---------------------------------------------------------------------------*/ +LABELEDLIST NewLabeledList ( + char *Label) + +/* +** Parameters: +** Label label for new list +** Globals: none +** Operation: +** This routine allocates a new, empty labeled list and gives +** it the specified label. +** Return: New, empty labeled list. +** Exceptions: none +** History: Fri Aug 18 16:08:46 1989, DSJ, Created. +*/ + +{ + LABELEDLIST LabeledList; + + LabeledList = (LABELEDLIST) Emalloc (sizeof (LABELEDLISTNODE)); + LabeledList->Label = (char*)Emalloc (strlen (Label)+1); + strcpy (LabeledList->Label, Label); + LabeledList->List = NIL; + return (LabeledList); + +} /* NewLabeledList */ + +/*---------------------------------------------------------------------------*/ +MERGE_CLASS NewLabeledClass ( + char *Label) +{ + MERGE_CLASS MergeClass; + + MergeClass = (MERGE_CLASS) Emalloc (sizeof (MERGE_CLASS_NODE)); + MergeClass->Label = (char*)Emalloc (strlen (Label)+1); + strcpy (MergeClass->Label, Label); + MergeClass->Class = NewClass (MAX_NUM_PROTOS, MAX_NUM_CONFIGS); + return (MergeClass); + +} /* NewLabeledClass */ + +/*---------------------------------------------------------------------------*/ +void WriteTrainingSamples ( + char *Directory, + LIST CharList) + +/* +** Parameters: +** Directory directory to place sample files into +** FontList list of fonts used in the training samples +** Globals: +** MaxNumSamples max number of samples per class to write +** Operation: +** This routine writes the specified samples into files which +** are organized according to the font name and character name +** of the samples. +** Return: none +** Exceptions: none +** History: Fri Aug 18 16:17:06 1989, DSJ, Created. +*/ + +{ + LABELEDLIST CharSample; + FEATURE_SET FeatureSet; + LIST FeatureList; + FILE *File; + char Filename[MAXNAMESIZE]; + int NumSamples; + + iterate (CharList) // iterate thru all of the fonts + { + CharSample = (LABELEDLIST) first (CharList); + + // construct the full pathname for the current samples file + strcpy (Filename, ""); + if (Directory != NULL) + { + strcat (Filename, Directory); + strcat (Filename, "/"); + } + strcat (Filename, FontName); + strcat (Filename, "/"); + strcat (Filename, CharSample->Label); + strcat (Filename, "."); + strcat (Filename, PROGRAM_FEATURE_TYPE); + printf ("\nWriting %s ...", Filename); + + /* if file does not exist, create a new one with an appropriate + header; otherwise append samples to the existing file */ + File = fopen (Filename, "r"); + if (File == NULL) + { + File = Efopen (Filename, "w"); + WriteOldParamDesc + (File, DefinitionOf (ShortNameToFeatureType (PROGRAM_FEATURE_TYPE))); + } + else + { + fclose (File); + File = Efopen (Filename, "a"); + } + + // append samples onto the file + FeatureList = CharSample->List; + NumSamples = 0; + iterate (FeatureList) + { + if (NumSamples >= MaxNumSamples) break; + + FeatureSet = (FEATURE_SET) first (FeatureList); + WriteFeatureSet (File, FeatureSet); + NumSamples++; + } + fclose (File); + } +} /* WriteTrainingSamples */ + + +/*----------------------------------------------------------------------------*/ +void WriteClusteredTrainingSamples ( + char *Directory, + LIST ProtoList, + CLUSTERER *Clusterer, + LABELEDLIST CharSample) + +/* +** Parameters: +** Directory directory to place sample files into +** Globals: +** MaxNumSamples max number of samples per class to write +** Operation: +** This routine writes the specified samples into files which +** are organized according to the font name and character name +** of the samples. +** Return: none +** Exceptions: none +** History: Fri Aug 18 16:17:06 1989, DSJ, Created. +*/ + +{ + FILE *File; + char Filename[MAXNAMESIZE]; + + strcpy (Filename, ""); + if (Directory != NULL) + { + strcat (Filename, Directory); + strcat (Filename, "/"); + } + strcat (Filename, FontName); + strcat (Filename, "/"); + strcat (Filename, CharSample->Label); + strcat (Filename, "."); + strcat (Filename, PROGRAM_FEATURE_TYPE); + strcat (Filename, ".p"); + printf ("\nWriting %s ...", Filename); + File = Efopen (Filename, "w"); + WriteProtoList(File, Clusterer->SampleSize, Clusterer->ParamDesc, + ProtoList, ShowSignificantProtos, ShowInsignificantProtos); + fclose (File); + +} /* WriteClusteredTrainingSamples */ + +/*---------------------------------------------------------------------------*/ +void WriteMergedTrainingSamples( + char *Directory, + LIST ClassList) + +{ + FILE *File; + char Filename[MAXNAMESIZE]; + MERGE_CLASS MergeClass; + + iterate (ClassList) + { + MergeClass = (MERGE_CLASS) first (ClassList); + strcpy (Filename, ""); + if (Directory != NULL) + { + strcat (Filename, Directory); + strcat (Filename, "/"); + } + strcat (Filename, "Merged/"); + strcat (Filename, MergeClass->Label); + strcat (Filename, PROTO_SUFFIX); + printf ("\nWriting Merged %s ...", Filename); + File = Efopen (Filename, "w"); + WriteOldProtoFile (File, MergeClass->Class); + fclose (File); + + strcpy (Filename, ""); + if (Directory != NULL) + { + strcat (Filename, Directory); + strcat (Filename, "/"); + } + strcat (Filename, "Merged/"); + strcat (Filename, MergeClass->Label); + strcat (Filename, CONFIG_SUFFIX); + printf ("\nWriting Merged %s ...", Filename); + File = Efopen (Filename, "w"); + WriteOldConfigFile (File, MergeClass->Class); + fclose (File); + } + +} // WriteMergedTrainingSamples + +/*--------------------------------------------------------------------------*/ +void WriteMicrofeat( + char *Directory, + LIST ClassList) + +{ + FILE *File; + char Filename[MAXNAMESIZE]; + MERGE_CLASS MergeClass; + + strcpy (Filename, ""); + if (Directory != NULL) + { + strcat (Filename, Directory); + strcat (Filename, "/"); + } + strcat (Filename, "Microfeat"); + File = Efopen (Filename, "w"); + printf ("\nWriting Merged %s ...", Filename); + iterate(ClassList) + { + MergeClass = (MERGE_CLASS) first (ClassList); + WriteProtos(File, MergeClass); + WriteConfigs(File, MergeClass->Class); + } + fclose (File); +} // WriteMicrofeat + +/*---------------------------------------------------------------------------*/ +void WriteProtos( + FILE* File, + MERGE_CLASS MergeClass) +{ + float Values[3]; + int i; + PROTO Proto; + + fprintf(File, "%c\n", NameToChar(MergeClass->Label)); + fprintf(File, "%d\n", NumProtosIn(MergeClass->Class)); + for(i=0; i < NumProtosIn(MergeClass->Class); i++) + { + Proto = ProtoIn(MergeClass->Class,i); + fprintf(File, "\t%8.4f %8.4f %8.4f %8.4f ", ProtoX(Proto), ProtoY(Proto), + ProtoLength(Proto), ProtoAngle(Proto)); + Values[0] = ProtoX(Proto); + Values[1] = ProtoY(Proto); + Values[2] = ProtoAngle(Proto); + Normalize(Values); + fprintf(File, "%8.4f %8.4f %8.4f\n", Values[0], Values[1], Values[2]); + } +} // WriteProtos + +/*----------------------------------------------------------------------------*/ +void WriteConfigs( + FILE* File, + CLASS_TYPE Class) +{ + BIT_VECTOR Config; + int i, j, WordsPerConfig; + + WordsPerConfig = WordsInVectorOfSize(NumProtosIn(Class)); + fprintf(File, "%d %d\n", NumConfigsIn(Class),WordsPerConfig); + for(i=0; i < NumConfigsIn(Class); i++) + { + Config = ConfigIn(Class,i); + for(j=0; j < WordsPerConfig; j++) + fprintf(File, "%08x ", Config[j]); + fprintf(File, "\n"); + } + fprintf(File, "\n"); +} // WriteConfigs + +/*---------------------------------------------------------------------------*/ +void FreeTrainingSamples ( + LIST CharList) + +/* +** Parameters: +** FontList list of all fonts in document +** Globals: none +** Operation: +** This routine deallocates all of the space allocated to +** the specified list of training samples. +** Return: none +** Exceptions: none +** History: Fri Aug 18 17:44:27 1989, DSJ, Created. +*/ + +{ + LABELEDLIST CharSample; + FEATURE_SET FeatureSet; + LIST FeatureList; + + + printf ("\nFreeTrainingSamples..."); + iterate (CharList) /* iterate thru all of the fonts */ + { + CharSample = (LABELEDLIST) first (CharList); + FeatureList = CharSample->List; + iterate (FeatureList) /* iterate thru all of the classes */ + { + FeatureSet = (FEATURE_SET) first (FeatureList); + FreeFeatureSet (FeatureSet); + } + FreeLabeledList (CharSample); + } + destroy (CharList); + +} /* FreeTrainingSamples */ + +/*-----------------------------------------------------------------------------*/ +void FreeLabeledClassList ( + LIST ClassList) + +/* +** Parameters: +** FontList list of all fonts in document +** Globals: none +** Operation: +** This routine deallocates all of the space allocated to +** the specified list of training samples. +** Return: none +** Exceptions: none +** History: Fri Aug 18 17:44:27 1989, DSJ, Created. +*/ + +{ + MERGE_CLASS MergeClass; + + iterate (ClassList) /* iterate thru all of the fonts */ + { + MergeClass = (MERGE_CLASS) first (ClassList); + free (MergeClass->Label); + FreeClass(MergeClass->Class); + free (MergeClass); + } + destroy (ClassList); + +} /* FreeLabeledClassList */ + +/*---------------------------------------------------------------------------*/ +void FreeLabeledList ( + LABELEDLIST LabeledList) + +/* +** Parameters: +** LabeledList labeled list to be freed +** Globals: none +** Operation: +** This routine deallocates all of the memory consumed by +** a labeled list. It does not free any memory which may be +** consumed by the items in the list. +** Return: none +** Exceptions: none +** History: Fri Aug 18 17:52:45 1989, DSJ, Created. +*/ + +{ + destroy (LabeledList->List); + free (LabeledList->Label); + free (LabeledList); + +} /* FreeLabeledList */ + +/*---------------------------------------------------------------------------*/ +CLUSTERER *SetUpForClustering( + LABELEDLIST CharSample) + +/* +** Parameters: +** CharSample: LABELEDLIST that holds all the feature information for a +** given character. +** Globals: +** None +** Operation: +** This routine reads samples from a LABELEDLIST and enters +** those samples into a clusterer data structure. This +** data structure is then returned to the caller. +** Return: +** Pointer to new clusterer data structure. +** Exceptions: +** None +** History: +** 8/16/89, DSJ, Created. +*/ + +{ + UINT16 N; + int i, j; + FLOAT32 *Sample = NULL; + CLUSTERER *Clusterer; + INT32 CharID; + LIST FeatureList = NULL; + FEATURE_SET FeatureSet = NULL; + FEATURE_DESC FeatureDesc = NULL; +// PARAM_DESC* ParamDesc; + + FeatureDesc = DefinitionOf(ShortNameToFeatureType(PROGRAM_FEATURE_TYPE)); + N = FeatureDesc->NumParams; +// ParamDesc = ConvertToPARAMDESC(FeatureDesc->ParamDesc, N); + Clusterer = MakeClusterer(N,FeatureDesc->ParamDesc); +// free(ParamDesc); + + FeatureList = CharSample->List; + CharID = 0; + iterate(FeatureList) + { + if (CharID >= MaxNumSamples) break; + + FeatureSet = (FEATURE_SET) first (FeatureList); + for (i=0; i < FeatureSet->MaxNumFeatures; i++) + { + if (Sample == NULL) + Sample = (FLOAT32 *)Emalloc(N * sizeof(FLOAT32)); + for (j=0; j < N; j++) + if (RoundingAccuracy != 0.0) + Sample[j] = round(FeatureSet->Features[i]->Params[j], RoundingAccuracy); + else + Sample[j] = FeatureSet->Features[i]->Params[j]; + MakeSample (Clusterer, Sample, CharID); + } + CharID++; + } + if ( Sample != NULL ) free( Sample ); + return( Clusterer ); + +} /* SetUpForClustering */ + +/*------------------------------------------------------------------------*/ +LIST RemoveInsignificantProtos( + LIST ProtoList, + BOOL8 KeepSigProtos, + BOOL8 KeepInsigProtos, + int N) + +{ + LIST NewProtoList = NIL; + LIST pProtoList; + PROTOTYPE* Proto; + PROTOTYPE* NewProto; + int i; + + pProtoList = ProtoList; + iterate(pProtoList) + { + Proto = (PROTOTYPE *) first (pProtoList); + if ((Proto->Significant && KeepSigProtos) || + (!Proto->Significant && KeepInsigProtos)) + { + NewProto = (PROTOTYPE *)Emalloc(sizeof(PROTOTYPE)); + + NewProto->Mean = (FLOAT32 *)Emalloc(N * sizeof(FLOAT32)); + NewProto->Significant = Proto->Significant; + NewProto->Style = Proto->Style; + NewProto->NumSamples = Proto->NumSamples; + NewProto->Cluster = NULL; + NewProto->Distrib = NULL; + + for (i=0; i < N; i++) + NewProto->Mean[i] = Proto->Mean[i]; + if (Proto->Variance.Elliptical != NULL) + { + NewProto->Variance.Elliptical = (FLOAT32 *)Emalloc(N * sizeof(FLOAT32)); + for (i=0; i < N; i++) + NewProto->Variance.Elliptical[i] = Proto->Variance.Elliptical[i]; + } + else + NewProto->Variance.Elliptical = NULL; + //--------------------------------------------- + if (Proto->Magnitude.Elliptical != NULL) + { + NewProto->Magnitude.Elliptical = (FLOAT32 *)Emalloc(N * sizeof(FLOAT32)); + for (i=0; i < N; i++) + NewProto->Magnitude.Elliptical[i] = Proto->Magnitude.Elliptical[i]; + } + else + NewProto->Magnitude.Elliptical = NULL; + //------------------------------------------------ + if (Proto->Weight.Elliptical != NULL) + { + NewProto->Weight.Elliptical = (FLOAT32 *)Emalloc(N * sizeof(FLOAT32)); + for (i=0; i < N; i++) + NewProto->Weight.Elliptical[i] = Proto->Weight.Elliptical[i]; + } + else + NewProto->Weight.Elliptical = NULL; + + NewProto->TotalMagnitude = Proto->TotalMagnitude; + NewProto->LogMagnitude = Proto->LogMagnitude; + NewProtoList = push_last(NewProtoList, NewProto); + } + } + //FreeProtoList (ProtoList); + return (NewProtoList); +} /* RemoveInsignificantProtos */ +/*-----------------------------------------------------------------------------*/ +void CleanUpUnusedData( + LIST ProtoList) +{ + PROTOTYPE* Prototype; + + iterate(ProtoList) + { + Prototype = (PROTOTYPE *) first (ProtoList); + if(Prototype->Variance.Elliptical != NULL) + { + memfree(Prototype->Variance.Elliptical); + Prototype->Variance.Elliptical = NULL; + } + if(Prototype->Magnitude.Elliptical != NULL) + { + memfree(Prototype->Magnitude.Elliptical); + Prototype->Magnitude.Elliptical = NULL; + } + if(Prototype->Weight.Elliptical != NULL) + { + memfree(Prototype->Weight.Elliptical); + Prototype->Weight.Elliptical = NULL; + } + } +} + +/*--------------------------------------------------------------------------*/ +void Normalize ( + float *Values) +{ + register float Slope; + register float Intercept; + register float Normalizer; + + Slope = tan (Values [2] * 2 * PI); + Intercept = Values [1] - Slope * Values [0]; + Normalizer = 1 / sqrt (Slope * Slope + 1.0); + + Values [0] = Slope * Normalizer; + Values [1] = - Normalizer; + Values [2] = Intercept * Normalizer; +} // Normalize + +/** SetUpForFloat2Int **************************************************/ +void SetUpForFloat2Int( + LIST LabeledClassList) +{ + MERGE_CLASS MergeClass; + CLASS_TYPE Class; + int NumProtos; + int NumConfigs; + int NumWords; + int i, j; + float Values[3]; + PROTO NewProto; + PROTO OldProto; + BIT_VECTOR NewConfig; + BIT_VECTOR OldConfig; + + printf("Float2Int ..."); + + iterate(LabeledClassList) + { + MergeClass = (MERGE_CLASS) first (LabeledClassList); + Class = &TrainingData[NameToChar(MergeClass->Label)]; + NumProtos = NumProtosIn(MergeClass->Class); + NumConfigs = NumConfigsIn(MergeClass->Class); + + NumProtosIn(Class) = NumProtos; + Class->MaxNumProtos = NumProtos; + Class->Prototypes = (PROTO) Emalloc (sizeof(PROTO_STRUCT) * NumProtos); + for(i=0; i < NumProtos; i++) + { + NewProto = ProtoIn(Class, i); + OldProto = ProtoIn(MergeClass->Class, i); + Values[0] = ProtoX(OldProto); + Values[1] = ProtoY(OldProto); + Values[2] = ProtoAngle(OldProto); + Normalize(Values); + ProtoX(NewProto) = ProtoX(OldProto); + ProtoY(NewProto) = ProtoY(OldProto); + ProtoLength(NewProto) = ProtoLength(OldProto); + ProtoAngle(NewProto) = ProtoAngle(OldProto); + CoefficientA(NewProto) = Values[0]; + CoefficientB(NewProto) = Values[1]; + CoefficientC(NewProto) = Values[2]; + } + + NumConfigsIn(Class) = NumConfigs; + Class->MaxNumConfigs = NumConfigs; + Class->Configurations = (BIT_VECTOR*) Emalloc (sizeof(BIT_VECTOR) * NumConfigs); + NumWords = WordsInVectorOfSize(NumProtos); + for(i=0; i < NumConfigs; i++) + { + NewConfig = NewBitVector(NumProtos); + OldConfig = ConfigIn(MergeClass->Class, i); + for(j=0; j < NumWords; j++) + NewConfig[j] = OldConfig[j]; + ConfigIn(Class, i) = NewConfig; + } + } +} // SetUpForFloat2Int diff --git a/training/mfTraining.dsp b/training/mfTraining.dsp new file mode 100644 index 0000000000..041c34ae33 --- /dev/null +++ b/training/mfTraining.dsp @@ -0,0 +1,269 @@ +# Microsoft Developer Studio Project File - Name="mfTraining" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=mfTraining - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mfTraining.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mfTraining.mak" CFG="mfTraining - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mfTraining - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "mfTraining - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mfTraining - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\release" +# PROP Intermediate_Dir "..\..\release\train" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../ccutil" /I "../ccstruct" /I "../classify" /I "../cutil" /I "../training" /I "../viewer" /I "../dict" /D "TRAINING" /D "WIN32" /D "_WINDOWS" /D "__NT__" /D "__MSW32__" /D "_AFXDLL" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 /nologo /subsystem:console /profile /debug /machine:I386 + +!ELSEIF "$(CFG)" == "mfTraining - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\Debug" +# PROP Intermediate_Dir "..\..\Debug\train" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../ccutil" /I "../ccstruct" /I "../classify" /I "../cutil" /I "../training" /I "../viewer" /I "../dict" /D "_DEBUG" /D "TRAINING" /D "WIN32" /D "_WINDOWS" /D "__NT__" /D "__MSW32__" /D "_AFXDLL" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:console /profile /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "mfTraining - Win32 Release" +# Name "mfTraining - Win32 Debug" +# Begin Source File + +SOURCE=..\cutil\bitvec.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\clst.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\cluster.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\clusttool.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\cutil.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\danerror.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\debug.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\debugwin.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\efio.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\emalloc.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\errcode.cpp +# End Source File +# Begin Source File + +SOURCE=..\viewer\evntlst.cpp +# End Source File +# Begin Source File + +SOURCE=..\viewer\evnts.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\featdefs.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\freelist.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\getopt.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\globaloc.cpp +# End Source File +# Begin Source File + +SOURCE=..\viewer\grphics.cpp +# End Source File +# Begin Source File + +SOURCE=..\viewer\grphshm.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\hashfn.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\intproto.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\kdtree.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\listio.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\memblk.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\memry.cpp +# End Source File +# Begin Source File + +SOURCE=.\mergenf.cpp + +!IF "$(CFG)" == "mfTraining - Win32 Release" + +!ELSEIF "$(CFG)" == "mfTraining - Win32 Debug" + +# SUBTRACT CPP /YX + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\ccutil\mfcpch.cpp +# End Source File +# Begin Source File + +SOURCE=.\mfTraining.cpp + +!IF "$(CFG)" == "mfTraining - Win32 Release" + +!ELSEIF "$(CFG)" == "mfTraining - Win32 Debug" + +# SUBTRACT CPP /YX + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\training\name2char.cpp + +!IF "$(CFG)" == "mfTraining - Win32 Release" + +!ELSEIF "$(CFG)" == "mfTraining - Win32 Debug" + +# SUBTRACT CPP /YX + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\classify\ocrfeatures.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\oldheap.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\oldlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\classify\protos.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\strngs.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\structures.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\tprintf.cpp +# End Source File +# Begin Source File + +SOURCE=..\training\training.cpp +# End Source File +# Begin Source File + +SOURCE=..\ccutil\varable.cpp +# End Source File +# Begin Source File + +SOURCE=..\cutil\variables.cpp +# End Source File +# End Target +# End Project diff --git a/training/name2char.cpp b/training/name2char.cpp new file mode 100644 index 0000000000..d232f98301 --- /dev/null +++ b/training/name2char.cpp @@ -0,0 +1,166 @@ +/****************************************************************************** +** Filename: name2char.c +** Purpose: Routines to convert between classes and class names. +** Author: Dan Johnson +** History: Fri Feb 23 08:03:09 1990, DSJ, Created. +** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. +******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "name2char.h" +#include "matchdefs.h" +#include "danerror.h" +#include + +#define ILLEGALCHARNAME 6001 + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* character ID (ascii code) to character name mapping */ +static const char *NameList[]={ + "!bang", + "\"doubleq", + "#hash", + "$dollar", + "%percent", + "&and", + "'quote", + "(lround", + ")rround", + "*asterisk", + "+plus", + ",comma", + "-minus", + ".dot", + "/slash", + ":colon", + ";semic", + "greater", + "?question", + "@at", + "[lsquare", + "\\backsl", + "]rsquare", + "^uparr", + "_unders", + "`grave", + "{lbrace", + "|bar", + "}rbrace", + "~tilde", + "AcA", + "BcB", + "CcC", + "DcD", + "EcE", + "FcF", + "GcG", + "HcH", + "IcI", + "JcJ", + "KcK", + "LcL", + "McM", + "NcN", + "OcO", + "PcP", + "QcQ", + "RcR", + "ScS", + "TcT", + "UcU", + "VcV", + "WcW", + "XcX", + "YcY", + "ZcZ", + NULL + }; + + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +CLASS_ID NameToChar ( + char CharName[]) + +/* +** Parameters: +** CharName character name to convert to a character +** Globals: +** NameList lookup table for name to char mapping +** Operation: +** This routine converts the specified character name to +** an ascii character. +** Return: Ascii character that corresponds to the character name. +** Exceptions: ILLEGALCHARNAME +** History: Sat Aug 26 12:26:54 1989, DSJ, Created. +*/ + +{ + int i; + + // look for name in table and return character if found + for ( i = 0; NameList[i] != NULL; i++ ) + if ( strcmp (CharName, &NameList[i][1]) == 0) + return (NameList[i][0]); + if ( strlen (CharName) == 1 ) + return (CharName[0]); //name is not in table but is a single character + else //illegal character + { + DoError (ILLEGALCHARNAME, "Illegal character name"); + return 0; + } +} /* NameToChar */ + +/*---------------------------------------------------------------------------*/ +void CharToName ( + CLASS_ID Char, + char CharName[]) + +/* +** Parameters: +** Char character to map to a character name +** CharName string to copy character name into +** Globals: +** NameList lookup table for char to name mapping +** Operation: +** This routine converts the specified ascii character to a +** character name. This is convenient for representing +** characters which might have special meaning to operating +** system shells or other programs (e.g. "*?&><" etc.). +** Return: none +** Exceptions: none +** History: Sat Aug 26 12:51:02 1989, DSJ, Created. +*/ + +{ + int i; + + /* look for character in table and return a copy of its name if found */ + for ( i = 0; NameList[i] != NULL; i++ ) + if ( Char == NameList[i][0] ) + { + strcpy ( CharName, &NameList[i][1] ); + return; + } + + /* if the character is not in the table, then use it as the name */ + CharName[0] = Char; + CharName[1] = 0; + +} /* CharToName */ diff --git a/training/name2char.h b/training/name2char.h new file mode 100644 index 0000000000..8cb1f3cde2 --- /dev/null +++ b/training/name2char.h @@ -0,0 +1,38 @@ +/****************************************************************************** +** Filename: name2char.h +** Purpose: Routines to convert between classes and class names. +** Author: Dan Johnson +** History: Fri Feb 23 08:10:40 1990, DSJ, Created. +** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. +******************************************************************************/ +#ifndef __NAME2CHAR__ +#define __NAME2CHAR__ + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "matchdefs.h" + + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +CLASS_ID NameToChar ( + char CharName[]); + +void CharToName ( + CLASS_ID Char, + char CharName[]); + + +#endif diff --git a/training/training.cpp b/training/training.cpp new file mode 100644 index 0000000000..652581bfc4 --- /dev/null +++ b/training/training.cpp @@ -0,0 +1,198 @@ +/* + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "training.h" +#include "debug.h" +#include "memry.h" +#include "grphics.h" +#include "evnts.h" + +make_int_var (LearningDebugLevel, 0, MakeLearningDebugLevel, + 18, 5, SetLearningDebugLevel, + "Learning Debug Level: "); + +make_int_var (NormMethod, character, MakeNormMethod, + 15, 10, SetNormMethod, "Normalization Method ...") + +char *demodir; /*demo home directory */ + + +void cprintf( //Trace printf +const char *format,... //special message +) +{ +} + +char *c_alloc_string( //allocate string +INT32 count //no of chars required +) +{ + return alloc_string(count); +} + +void c_free_string( //free a string +char *string //string to free +) +{ + free_string(string); +} + +void *c_alloc_mem_p( //allocate permanent space +INT32 count //block size to allocate +) +{ + return alloc_mem_p(count); +} +void *c_alloc_mem( //get some memory +INT32 count //no of bytes to get +) +{ + return alloc_mem(count); +} +void c_free_mem( //free mem from alloc_mem +void *oldchunk //chunk to free +) +{ + free_mem(oldchunk); +} +void c_check_mem( //check consistency +const char *string, //context message +INT8 level //level of check +) +{ + check_mem(string,level); +} + +void* c_alloc_struct( //allocate memory +INT32 count, //no of chars required +const char* name //class name +) +{ + return alloc_struct(count,name); +} +void c_free_struct( //free a structure +void* deadstruct, //structure to free +INT32 count, //no of bytes +const char* name //class name +) +{ + free_struct(deadstruct,count,name); +} + +void c_make_current( /*move pen*/ +void* win +) +{ + WINDOW window=(WINDOW)win; + + window->Make_picture_current(); +} + +void reverse32( +void* ptr +) +{ + char tmp; + char* cptr=(char*)ptr; + + tmp=*cptr; + *cptr=*(cptr+3); + *(cptr+3)=tmp; + tmp=*(cptr+1); + *(cptr+1)=*(cptr+2); + *(cptr+2)=tmp; +} + +void reverse16( +void* ptr +) +{ + char tmp; + char* cptr=(char*)ptr; + + tmp=*cptr; + *cptr=*(cptr+1); + *(cptr+1)=tmp; +} + + + + + +void* c_create_window( /*create a window*/ +const char *name, /*name/title of window*/ +INT16 xpos, /*coords of window*/ +INT16 ypos, /*coords of window*/ +INT16 xsize, /*size of window*/ +INT16 ysize, /*size of window*/ +double xmin, /*scrolling limits*/ +double xmax, /*to stop users*/ +double ymin, /*getting lost in*/ +double ymax /*empty space*/ +) +{ + return create_window(name,SCROLLINGWIN,xpos,ypos,xsize,ysize, + xmin,xmax,ymin,ymax,TRUE,FALSE,FALSE,TRUE); +} +void c_line_color_index( /*set color*/ +void* win, +C_COL index +) +{ + WINDOW window=(WINDOW)win; + +// ASSERT_HOST(index>=0 && index<=48); + if (index<0 || index>48) + index=(C_COL)1; + window->Line_color_index((COLOUR)index); +} +void c_move( /*move pen*/ +void* win, +double x, +double y +) +{ + WINDOW window=(WINDOW)win; + + window->Move2d(x,y); +} +void c_draw( /*move pen*/ +void* win, +double x, +double y +) +{ + WINDOW window=(WINDOW)win; + + window->Draw2d(x,y); +} +void c_clear_window( /*move pen*/ +void* win +) +{ + WINDOW window=(WINDOW)win; + + window->Clear_view_surface(); +} +char window_wait( /*move pen*/ +void* win +) +{ + WINDOW window=(WINDOW)win; + GRAPHICS_EVENT event; + + await_event(window,TRUE,ANY_EVENT,&event); + if (event.type==KEYPRESS_EVENT) + return event.key; + else + return '\0'; +} diff --git a/training/training.h b/training/training.h new file mode 100644 index 0000000000..66cccdc77b --- /dev/null +++ b/training/training.h @@ -0,0 +1,130 @@ +/* + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#ifndef TRAINING_H +#define TRAINING_H + +#include "host.h" +#include "callcpp.h" + +typedef enum +{ +baseline, character +} +NORM_METHOD; + +typedef struct +{ +FLOAT32 x,y; +} FPOINT; +typedef FPOINT FVECTOR; + + +#define INTEL 0x4949 +#define MOTO 0x4d4d + +#define PICO_FEATURE_LENGTH (0.05) +#define GetPicoFeatureLength() (PICO_FEATURE_LENGTH) + +extern int LearningDebugLevel; +extern int NormMethod; + +void cprintf( //Trace printf +const char *format,... //special message +); + +char *c_alloc_string( //allocate string +INT32 count //no of chars required +); + +void c_free_string( //free a string +char *string //string to free +); + +void *c_alloc_mem_p( //allocate permanent space +INT32 count //block size to allocate +); + +void *c_alloc_mem( //get some memory +INT32 count //no of bytes to get +); + +void c_free_mem( //free mem from alloc_mem +void *oldchunk //chunk to free +); + +void c_check_mem( //check consistency +char *string, //context message +INT8 level //level of check +); + +void* c_alloc_struct( //allocate memory +INT32 count, //no of chars required +const char* name //class name +); + +void c_free_struct( //free a structure +void* deadstruct, //structure to free +INT32 count, //no of bytes +const char* name //class name +); + +void c_make_current( /*move pen*/ +void* win +); + +void reverse32( +void* ptr +); + +void reverse16( +void* ptr +); + +void* c_create_window( /*create a window*/ +const char *name, /*name/title of window*/ +INT16 xpos, /*coords of window*/ +INT16 ypos, /*coords of window*/ +INT16 xsize, /*size of window*/ +INT16 ysize, /*size of window*/ +double xmin, /*scrolling limits*/ +double xmax, /*to stop users*/ +double ymin, /*getting lost in*/ +double ymax /*empty space*/ +); + +void c_line_color_index( /*set color*/ +void* win, +C_COL index +); + +void c_move( /*move pen*/ +void* win, +double x, +double y +); + +void c_draw( /*move pen*/ +void* win, +double x, +double y +); + +void c_clear_window( /*move pen*/ +void* win +); + +char window_wait( /*move pen*/ +void* win +); + +#endif diff --git a/viewer/Makefile.am b/viewer/Makefile.am new file mode 100644 index 0000000000..83a08eba0c --- /dev/null +++ b/viewer/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/image -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + evntlst.h evnts.h grphics.h grphshm.h sbgconst.h sbgdefs.h \ + sbgtypes.h showim.h + +noinst_LIBRARIES = libtesseract_viewer.a +libtesseract_viewer_a_SOURCES = \ + evntlst.cpp grphics.cpp evnts.cpp showim.cpp grphshm.cpp diff --git a/viewer/Makefile.in b/viewer/Makefile.in new file mode 100644 index 0000000000..dcc6732449 --- /dev/null +++ b/viewer/Makefile.in @@ -0,0 +1,531 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = viewer +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_viewer_a_AR = $(AR) $(ARFLAGS) +libtesseract_viewer_a_LIBADD = +am_libtesseract_viewer_a_OBJECTS = evntlst.$(OBJEXT) grphics.$(OBJEXT) \ + evnts.$(OBJEXT) showim.$(OBJEXT) grphshm.$(OBJEXT) +libtesseract_viewer_a_OBJECTS = $(am_libtesseract_viewer_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_viewer_a_SOURCES) +DIST_SOURCES = $(libtesseract_viewer_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/image -I$(top_srcdir)/ccutil +EXTRA_DIST = \ + evntlst.h evnts.h grphics.h grphshm.h sbgconst.h sbgdefs.h \ + sbgtypes.h showim.h + +noinst_LIBRARIES = libtesseract_viewer.a +libtesseract_viewer_a_SOURCES = \ + evntlst.cpp grphics.cpp evnts.cpp showim.cpp grphshm.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu viewer/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu viewer/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_viewer.a: $(libtesseract_viewer_a_OBJECTS) $(libtesseract_viewer_a_DEPENDENCIES) + -rm -f libtesseract_viewer.a + $(libtesseract_viewer_a_AR) libtesseract_viewer.a $(libtesseract_viewer_a_OBJECTS) $(libtesseract_viewer_a_LIBADD) + $(RANLIB) libtesseract_viewer.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/evntlst.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/evnts.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grphics.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grphshm.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/showim.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/viewer/evntlst.cpp b/viewer/evntlst.cpp new file mode 100644 index 0000000000..bc005817b6 --- /dev/null +++ b/viewer/evntlst.cpp @@ -0,0 +1,344 @@ +/********************************************************************** + * File: evntlst.c (Formerly eventlst.c) + * Description: Code to manipulate lists of events. + * Author: Ray Smith + * Created: Fri Nov 01 11:02:52 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "grphshm.h" +#include "grphics.h" +#ifdef __UNIX__ +#include +#include +//#include "pipes.h" +#include "fileerr.h" +//#include "sbderrs.h" +//#include "grpherr.h" +#elif defined (__MSW32__) +#include +#include +#else +#include +#endif +#include "evntlst.h" + +#define EXTERN + //anything new in queue +EXTERN BOOL8 event_waiting = FALSE; + +#ifdef __UNIX__ + //semaphore count +static INT32 event_critical_section = 0; + //true if pending +static BOOL8 event_pending = FALSE; +EXTERN BOOL8 handler_set; //true if signal handler on +EXTERN STRING_VAR (events_logfile, "", "File to log events to"); +EXTERN STRING_VAR (events_playback, "", "File to read events from"); + +/********************************************************************** + * event_handler + * + * Handler for signal. + * If not critical, read events now, otherwise, set pending flag. + **********************************************************************/ + +void event_handler( //signal handler + int, //signal + int, //code + struct sigcontext *scp //info for sigvector + ) { + // scp->sc_syscall_action=SIG_RESTART; //restart sys calls + event_pending = TRUE; + if (!event_critical_section) { + lock_events(); + unlock_events(); + } +} + + +/********************************************************************** + * check_event + * + * See if any events are available, and if there are, return 1, else 0. + **********************************************************************/ + +BOOL8 check_event( /*test for event */ + INT16 fd, /*window to wait on */ + BOOL8 wait /*set if waiting */ + ) { + GRAPHICS_EVENT event; /*event from daemon */ + SBD_GRAPHICS_EVENT sbd_event; //event from pipe + BOOL8 gotone; /*repeat while do */ + INT32 time; /*time to select */ + INT32 typein; //input type + INT32 keyin; //input key + INT32 fdin; //input fd + int scanresult; //of fscanf + static FILE *eventsin = NULL; //input file + + // && sbfds[fd].used!=1) + if (fd < 0 || fd > maxsbfd || fd > 0) { + // BADSBFD.error("check_event",LOG,NULL); /*report error*/ + return FALSE; + } + time = wait ? AWAIT_TIME : CHECK_TIME; + gotone = FALSE; + while (!gotone + && (events_playback.string ()[0] != '\0' || eventsin != NULL)) { + if (eventsin == NULL && events_playback.string ()[0] != '\0') { + if ((eventsin = fopen (events_playback.string (), "r")) == NULL) + CANTOPENFILE.error ("check_event", LOG, + events_playback.string ()); + //remove name + events_playback.set_value (NULL); + } + if (eventsin != NULL) { + scanresult = + fscanf (eventsin, + INT32FORMAT " " INT32FORMAT "(%f,%f) on " INT32FORMAT, + &typein, &keyin, &event.x, &event.y, &fdin); + if (scanresult != 5) { + if (scanresult == EOF) + fprintf (stderr, "Closing input event file\n"); + else + fprintf (stderr, "Error on event input: copied %d values\n", + scanresult); + fclose(eventsin); + eventsin = NULL; + } + else { + // fprintf(stderr,"Read input on %d\n", fdin ); + event.type = (INT8) typein; + event.key = (char) keyin; + event.fildes = fdin; + add_event(&event); //got one + gotone = gotone || fd == 0 || fd == event.fildes; + /*know if we got one */ + } + } + } + while (!gotone) { // && select_read(shminfo.fds[INFD],time)!=0) /*event available*/ + if (read (shminfo.fds[INFD], &sbd_event, sizeof (SBD_GRAPHICS_EVENT)) + != sizeof (SBD_GRAPHICS_EVENT)) + READFAILED.error ("check_event", EXIT, "sbdaemon pipe"); + if (sbd_event.type != QUEUE_CLEAR) { + event.fildes = sbd_event.fd; + event.type = sbd_event.type; + event.key = sbd_event.key; + event.x = sbd_event.x; + event.y = sbd_event.y; + event.next = NULL; + add_event(&event); /*add event to queue */ + /*know if we got one */ + gotone = gotone || fd == 0 || fd == event.fildes; + } + else + kick_daemon(COUNT_READS); /*keep count accurate */ + time = CHECK_TIME; + } + return gotone; +} + + +#else +EXTERN HANDLE event_sem = NULL; //event lock + +/********************************************************************** + * event_reader + * + * A separate thread that reads the input from the pipe and places + * events in the appropriate queue. This thread also calls any appropriate + * event handler on receiving an event. + **********************************************************************/ + +void event_reader( /*read events */ + void *param /*file descriptor */ + ) { + #ifndef __MAC__ + //real file descriptor + HANDLE *fdptr = (HANDLE *) param; + HANDLE fd = *fdptr; //real file descriptor + unsigned long nread; //bytes read + SBD_GRAPHICS_EVENT event; /*event from daemon */ + GRAPHICS_EVENT real_event; //converted format + char pipe_char[2]; //from pipe + INT32 pipe_index; //index to event queue + + event_id = GetCurrentThreadId (); + while (ReadFile (fd, pipe_char, 2, &nread, NULL) != 0 && nread == 2) { + pipe_index = EVENT_HEAD; + event = EVENT_INDEX (pipe_index); + pipe_index++; + if (pipe_index >= EVENTSIZE) + pipe_index = 0; + EVENT_HEAD = pipe_index; + if (event.type != QUEUE_CLEAR) { + real_event.fildes = event.fd; + real_event.type = event.type; + real_event.key = event.key; + real_event.x = event.x; + real_event.y = event.y; + real_event.next = NULL; + add_event(&real_event); /*add event to queue */ + } + else + kick_daemon(COUNT_READS); /*got acknowledge */ + } + CloseHandle(fd); + *fdptr = NULL; + _endthread(); + #endif +} +#endif + +/********************************************************************** + * add_event + * + * Adds an event from the sbdaemon to the correct event queue. + **********************************************************************/ + +void add_event( /*add an event */ + GRAPHICS_EVENT *event /*event to add */ + ) { + GRAPHICS_EVENT *newevent; /*new event */ + EVENT_HANDLER handler; //avoid race hazards + GRAPHICS_EVENT sel_event; //selection + //last button down + static GRAPHICS_EVENT last_down; + + #ifdef __UNIX__ + static FILE *eventsout = NULL; //output file + static STRING outname; //output name + + //doing anything + if (events_logfile.string ()[0] != '\0' || eventsout != NULL) { + if (eventsout != NULL //already open + && outname != (STRING &) events_logfile) { + fclose(eventsout); //finished that one + eventsout = NULL; + } + //needs opening + if (eventsout == NULL && events_logfile.string ()[0] != '\0') { + //save name + outname = events_logfile.string (); + if ((eventsout = fopen (outname.string (), "w")) == NULL) + CANTOPENFILE.error ("add_event", LOG, outname.string ()); + } + if (eventsout != NULL) + fprintf (eventsout, "%d %d(%f,%f) on %d\n", + event->type, event->key, event->x, event->y, event->fildes); + } + #endif + // fprintf(stderr,"Received event of type %d at (%f,%f) on %d\n", + // event->type,event->x,event->y,event->fildes); + event->fd = &sbfds[event->fildes]; + switch (event->type) { + case DOWN_EVENT: + last_down = *event; //save it + handler = sbfds[event->fildes].click_handler; + if (handler != NULL) { + (*handler) (event); + return; //done it + } + break; + case UP_EVENT: + sel_event = *event; + if (last_down.x > event->x) + sel_event.xmax = last_down.x; + else { + sel_event.xmax = event->x; + sel_event.x = last_down.x; + } + if (last_down.y > event->y) + sel_event.ymax = last_down.y; + else { + sel_event.ymax = event->y; + sel_event.y = last_down.y; + } + handler = sbfds[event->fildes].selection_handler; + if (handler != NULL) { + (*handler) (&sel_event); + return; //done it + } + break; + case KEYPRESS_EVENT: + handler = sbfds[event->fildes].key_handler; + if (handler != NULL) { + (*handler) (event); + return; //done it + } + break; + case DESTROY_EVENT: + handler = sbfds[event->fildes].destroy_handler; + if (handler != NULL) { + (*handler) (event); + // return; //done it + } + break; + } + lock_events(); + newevent = new GRAPHICS_EVENT; + if (newevent != NULL) { + *newevent = *event; /*copy event */ + /*first event */ + if (sbfds[event->fildes].events == NULL) + sbfds[event->fildes].events = newevent; + else + /*add to end */ + sbfds[event->fildes].lastevent->next = newevent; + /*is new end */ + sbfds[event->fildes].lastevent = newevent; + newevent->next = NULL; + } + event_waiting = TRUE; //added to queue + unlock_events(); +} + + +/********************************************************************** + * lock_events + * + * Lock out event handler to protect data structures. + **********************************************************************/ + +void lock_events() { //lock + #ifdef __UNIX__ + event_critical_section++; + #elif defined (__MSW32__) + WaitForSingleObject (event_sem, (unsigned long) -1); + #endif +} + + +/********************************************************************** + * unlock_events + * + * Unlock. If event pending deal with it. + **********************************************************************/ + +void unlock_events() { //lock + #ifdef __UNIX__ + if (event_pending && event_critical_section == 1) { + //get all events + while (check_event (0, FALSE)); + event_pending = FALSE; + } + event_critical_section--; + #elif defined (__MSW32__) + //release it + ReleaseSemaphore (event_sem, 1, NULL); + #endif +} diff --git a/viewer/evntlst.h b/viewer/evntlst.h new file mode 100644 index 0000000000..216d1361ab --- /dev/null +++ b/viewer/evntlst.h @@ -0,0 +1,51 @@ +/********************************************************************** + * File: evntlst.h (Formerly eventlst.h) + * Description: Code to manipulate lists of events. + * Author: Ray Smith + * Created: Fri Nov 01 11:02:52 GMT 1991 + * + * (C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef EVNTLST_H +#define EVNTLST_H + +#include "sbgtypes.h" +extern BOOL8 event_waiting; //anything new in queue +#ifdef __UNIX__ +#include "varable.h" + +extern BOOL8 handler_set; //true if signal handler on +extern STRING_VAR_H (events_logfile, "", "File to log events to"); +extern STRING_VAR_H (events_playback, "", "File to read events from"); +void event_handler( //signal handler + int, //signal + int, //code + struct sigcontext *scp //info for sigvector + ); +BOOL8 check_event( /*test for event */ + INT16 fd, /*window to wait on */ + BOOL8 wait /*set if waiting */ + ); +#else /* */ +extern HANDLE event_sem; //event lock +void event_reader( /*read events */ + void *param /*file descriptor */ + ); +#endif +void add_event( /*add an event */ + GRAPHICS_EVENT *event /*event to add */ + ); +void lock_events(); //lock +void unlock_events(); //lock +#endif diff --git a/viewer/evnts.cpp b/viewer/evnts.cpp new file mode 100644 index 0000000000..6ea5be6eb9 --- /dev/null +++ b/viewer/evnts.cpp @@ -0,0 +1,349 @@ +/********************************************************************** + * File: evnts.c (Formerly events.c) + * Description: Additional functions needed to receive graphics events. + * Author: Ray Smith + * Created: Thu May 24 14:13:00 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include "sbderrs.h" +#include "fileerr.h" +#include "grpherr.h" +#endif +#include "grphshm.h" +#include "evntlst.h" +#include "evnts.h" + +#define EXTERN +DLLSYM EVENT_HANDLER win_selection_handler; +WINDOW (*await_event_func) (WINDOW, BOOL8, INT8, GRAPHICS_EVENT *) = +def_await_event; + +//local non-public functions +GRAPHICS_EVENT *find_event( /*wait for event */ + INT16 &fd, /*window to wait on */ + BOOL8 wait, /*waiting flag */ + INT8 event_type /*type to look for */ + ); + /*search for event */ +GRAPHICS_EVENT *search_event_queue(INT16 &fd, /*queue to search */ + INT8 event_type /*type to search for */ + ); + /*search for event */ +GRAPHICS_EVENT *search_single_queue(INT16 fd, /*queue to search */ + INT8 event_type /*type to search for */ + ); + +/********************************************************************** + * await_selection + * + * Wait (or check) for a selection on the given (or all) fd and return it. + **********************************************************************/ + +DLLSYM WINDOW await_selection( /*wait for selection */ + WINDOW win, /*window to wait on */ + BOOL8 wait, /*waiting flag */ + float &xmin, /*coords of selection */ + float &ymin, /*coords of selection */ + float &xmax, /*coords of selection */ + float &ymax /*coords of selection */ + ) { + GRAPHICS_EVENT event; /*return event */ + + win = await_event (win, wait, SELECT_EVENT, &event); + if (event.type == DESTROY_EVENT) + return NULL; //was destroyed + if (win != NULL) { + xmin = event.x; /*get coords */ + ymin = event.y; + xmax = event.xmax; /*get coords */ + ymax = event.ymax; + } + return win; +} + + +/********************************************************************** + * await_click + * + * Wait (or check) for a click on the given (or all) fd and return it. + **********************************************************************/ + +DLLSYM WINDOW await_click( /*wait for click */ + WINDOW win, /*window to wait on */ + BOOL8 wait, /*waiting flag */ + float &x, /*coords of click */ + float &y /*coords of click */ + ) { + GRAPHICS_EVENT event; /*return event */ + + win = await_event (win, wait, DOWN_EVENT, &event); + if (event.type == DESTROY_EVENT) + return NULL; //was destroyed + if (win != NULL) { + x = event.x; /*get coords */ + y = event.y; + } + return win; +} + + +/********************************************************************** + * await_key + * + * Wait (or check) for a key on the given (or all) fd and return it. + **********************************************************************/ + +DLLSYM WINDOW await_key( /*wait for key */ + WINDOW win, /*window to wait on */ + BOOL8 wait, /*waiting flag */ + char &c /*return character */ + ) { + GRAPHICS_EVENT event; /*return event */ + + win = await_event (win, wait, KEYPRESS_EVENT, &event); + if (event.type == DESTROY_EVENT) + return NULL; //was destroyed + if (win != NULL) + c = event.key; /*get keypress */ + return win; +} + + +/********************************************************************** + * await_event + * + * Wait (or check) for a event on the given (or all) fd and return it. + **********************************************************************/ + +DLLSYM WINDOW def_await_event( /*wait for event */ + WINDOW win, /*window to wait on */ + BOOL8 wait, /*waiting flag */ + INT8 event_type, /*type to wait for */ + GRAPHICS_EVENT *out_event /*output event */ + ) { + GRAPHICS_EVENT *event; /*return event */ + INT16 fd; //file descriptor + + if (win == NULL) + fd = 0; + else + fd = win->get_fd (); + /*look for one */ + event = find_event (fd, wait, event_type); + if (event == NULL) + return NULL; /*not found */ + else { + *out_event = *event; /*copy event */ + if (event->type != DESTROY_EVENT) + delete event; //free the element + return out_event->fd; + } +} + + +/********************************************************************** + * find_event + * + * Search the queue for an event of a given type, and return it. + * Read or wait until one turns up if there is not one already. + **********************************************************************/ + +GRAPHICS_EVENT *find_event( /*wait for event */ + INT16 &fd, /*window to wait on */ + BOOL8 wait, /*waiting flag */ + INT8 event_type /*type to look for */ + ) { + GRAPHICS_EVENT *event; /*return event */ + + /*look for one */ + event = search_event_queue (fd, event_type); + if (event == NULL) { + do { + #ifdef __UNIX__ + if (check_event (fd, wait)) + #elif defined (__MSW32__) + if (wait) + Sleep (50); + if (event_waiting) + #endif + { + // fprintf(stderr,"Got an event:searching queue %d\n",fd); + /*try after reading */ + event = search_event_queue (fd, event_type); + } + } + while (wait && event == NULL); + } + // if (event!=NULL) + // event->fd=&sbfds[fd]; + return event; /*event located */ +} + + +/********************************************************************** + * search_event_queue + * + * Search the event queue(s) for events of a particular type. + * If found, it is removed from the queue and returned. + **********************************************************************/ + +GRAPHICS_EVENT *search_event_queue( /*search for event */ + INT16 &fd, /*queue to search */ + INT8 event_type /*type to search for */ + ) { + GRAPHICS_EVENT *event; /*event from daemon */ + INT16 testfd; /*test window */ + + if (fd < 0 || fd > maxsbfd || fd > 0 && sbfds[fd].used != 1) { + return NULL; + } + if (fd > 0) + /*just one to search */ + return search_single_queue (fd, event_type); + else { + for (testfd = 1; testfd < maxsbfd; testfd++) { + if (sbfds[testfd].used) { + event = search_single_queue (testfd, event_type); + if (event != NULL) { + fd = testfd; /*successful window */ + return event; /*got one */ + } + } + } + } + return NULL; /*found nothing */ +} + + +/********************************************************************** + * search_single_queue + * + * Search the event queue for events of a particular type. + * If found, it is removed from the queue and returned. + **********************************************************************/ + +GRAPHICS_EVENT *search_single_queue( /*search for event */ + INT16 fd, /*queue to search */ + INT8 event_type /*type to search for */ + ) { + GRAPHICS_EVENT *event; /*event from daemon */ + GRAPHICS_EVENT *prev; /*previous event */ + GRAPHICS_EVENT *event2; /*2nd event */ + GRAPHICS_EVENT *prev2; /*2nd previous */ + GRAPHICS_EVENT *nextevent; /*next event in list */ + BOOL8 any_destroy = FALSE; + + lock_events(); + event_waiting = FALSE; //done a scan + prev = NULL; /*previous event */ + event2 = NULL; /*2nd event */ + if (event_type == ANY_EVENT) { + event = sbfds[fd].events; /*start of queue */ + } + else if (event_type == SELECT_EVENT) { + for (prev = NULL, event = sbfds[fd].events; event != NULL + && event->type != DOWN_EVENT; event = nextevent) { + //Delete up events that are in the list prior to a down event + nextevent = event->next; /*next in list */ + if (event->type == UP_EVENT) { + if (prev == NULL) + /*new head */ + sbfds[fd].events = nextevent; + else + prev->next = nextevent;/*delete event */ + if (nextevent == NULL) + /*new last element */ + sbfds[fd].lastevent = prev; + delete event; + } + else + prev = event; + if (event->type == DESTROY_EVENT) + any_destroy = TRUE; + } + if (event == NULL) { + unlock_events(); + if (any_destroy) + return search_single_queue (fd, DESTROY_EVENT); + return NULL; /*no good */ + } + for (prev2 = event, event2 = event->next; event2 != NULL + && event2->type != UP_EVENT; + prev2 = event2, event2 = event2->next); + if (event2 == NULL) { + unlock_events(); + return NULL; /*no good */ + } + if (prev2 != event) { /*got some intervening */ + for (prev2 = event->next; prev2 != event2; prev2 = nextevent) { + nextevent = prev2->next; + delete prev2; + } + } + event->next = event2->next; /*cut out event2 */ + event2->next = NULL; /*event is new end */ + + event->xmax = event2->x; /*get coords */ + event->ymax = event2->y; + if (event->x > event->xmax) { + event->xmax = event->x; + event->x = event2->x; /*get coords */ + } + if (event->y > event->ymax) { + event->ymax = event->y; + event->y = event2->y; + } + delete event2; //free the element + } + else { + for (prev = NULL, event = sbfds[fd].events; event != NULL + && event->type != DESTROY_EVENT + && event->type != event_type; event = nextevent) { + nextevent = event->next; /*next in list */ + /*delete up in front of down */ + if (event->type == UP_EVENT && event_type == DOWN_EVENT) { + if (prev == NULL) + /*new head */ + sbfds[fd].events = nextevent; + else + prev->next = nextevent;/*delete event */ + if (nextevent == NULL) + /*new last element */ + sbfds[fd].lastevent = prev; + delete event; + } + else + prev = event; /*trailing ptr */ + } + } + if (event == NULL) { + unlock_events(); + return NULL; /*no good */ + } + if (event->type != DESTROY_EVENT) { + if (prev == NULL) + /*new head */ + sbfds[fd].events = event->next; + else + prev->next = event->next; /*delete event */ + if (event->next == NULL) + sbfds[fd].lastevent = prev;/*new last event */ + event->next = NULL; /*possible 2nd event */ + } + unlock_events(); + return event; /*got one!! */ +} diff --git a/viewer/evnts.h b/viewer/evnts.h new file mode 100644 index 0000000000..57fbaf3811 --- /dev/null +++ b/viewer/evnts.h @@ -0,0 +1,52 @@ +/********************************************************************** + * File: evnts.h (Formerly events.h) + * Description: Header of functions and types needed for using events. + * Author: Ray Smith + * Created: Thu May 24 15:14:45 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef EVNTS_H +#define EVNTS_H + +#include "grphics.h" + +extern DLLSYM EVENT_HANDLER win_selection_handler; + +DLLSYM WINDOW await_selection( /*wait for selection */ + WINDOW win, /*window to wait on */ + BOOL8 wait, /*waiting flag */ + float &xmin, /*coords of selection */ + float &ymin, /*coords of selection */ + float &xmax, /*coords of selection */ + float &ymax /*coords of selection */ + ); +DLLSYM WINDOW await_click( /*wait for click */ + WINDOW win, /*window to wait on */ + BOOL8 wait, /*waiting flag */ + float &x, /*coords of click */ + float &y /*coords of click */ + ); +DLLSYM WINDOW await_key( /*wait for key */ + WINDOW win, /*window to wait on */ + BOOL8 wait, /*waiting flag */ + char &c /*return character */ + ); +DLLSYM WINDOW def_await_event( /*wait for event */ + WINDOW win, /*window to wait on */ + BOOL8 wait, /*waiting flag */ + INT8 event_type, /*type to wait for */ + GRAPHICS_EVENT *out_event /*output event */ + ); +#endif diff --git a/viewer/grphics.cpp b/viewer/grphics.cpp new file mode 100644 index 0000000000..d59c3eb405 --- /dev/null +++ b/viewer/grphics.cpp @@ -0,0 +1,1008 @@ +/********************************************************************** + * File: grphics.c (Formerly graphics.c) + * Description: Starbase stubs for connection to sbdaemon + * Author: Ray Smith + * Created: Wed May 16 08:34:32 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include +#ifdef __UNIX__ +#include +#endif +#include "grphics.h" +//#include "sbderrs.h" +//#include "grpherr.h" +#include "grphshm.h" +#include "evntlst.h" + +#define XSIZE_INCREMENT 8 +#define YSIZE_INCREMENT 30 + +void def_overlap_picture_ops(BOOL8 update); + +WINCREATEFUNC create_func = WINFD::create; +void (*overlap_func) (BOOL8) = def_overlap_picture_ops; + +/********************************************************************** + * line_color_index + * + * Set the colour map index for drawing lines with. + **********************************************************************/ + +void WINFD::Line_color_index( /*set line colour */ + COLOUR index /*index to use */ + ) { + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = LINECOLOUR; /*send the operator */ + newop->param.p.i = index; /*set parameter */ + } +} + + +/********************************************************************** + * perimeter_color_index + * + * Set the colour map index for drawing perimeters with. + **********************************************************************/ + +void WINFD::Perimeter_color_index( /*set perimeter colour */ + COLOUR index /*index to use */ + ) { + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + /*send the operator */ + newop->type = PERIMETERCOLOUR; + newop->param.p.i = index; /*set parameter */ + } +} + + +/********************************************************************** + * fill_color_index + * + * Set the colour map index for drawing fills with. + **********************************************************************/ + +void WINFD::Fill_color_index( /*set fill colour */ + COLOUR index /*index to use */ + ) { + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = FILLCOLOUR; /*send the operator */ + newop->param.p.i = index; /*set parameter */ + } +} + + +/********************************************************************** + * fill_color + * + * Set the RGB colour for drawing fills with. + **********************************************************************/ + +void WINFD::Fill_color( /*set fill colour */ + UINT8 red, + UINT8 green, + UINT8 blue) { + ONEOP *newop; /*message structure */ + UINT32 packed_colour; + + packed_colour = (blue << 24) + (green << 16) + (red << 8); + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = FILLCOLOUR; /*send the operator */ + /*set parameter */ + newop->param.p.i = (INT32) packed_colour; + } +} + + +/********************************************************************** + * marker_color_index + * + * Set the colour map index for drawing markers with. + **********************************************************************/ + +void WINFD::Marker_color_index( /*set marker colour */ + COLOUR index /*index to use */ + ) { + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = MARKERCOLOUR; /*send the operator */ + newop->param.p.i = index; /*set parameter */ + } +} + + +/********************************************************************** + * text_color_index + * + * Set the colour map index for drawing texts with. + **********************************************************************/ + +void WINFD::Text_color_index( /*set text colour */ + COLOUR index /*index to use */ + ) { + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = TEXTCOLOUR; /*send the operator */ + newop->param.p.i = index; /*set parameter */ + } +} + + +/********************************************************************** + * text_font_index + * + * Set the text font index for drawing texts with. + **********************************************************************/ + +void WINFD::Text_font_index( /*set text font */ + INT16 index /*index to use */ + ) { + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = TEXTFONT; /*send the operator */ + newop->param.p.i = index; /*set parameter */ + } +} + + +/********************************************************************** + * character_height + * + * Set the VDC height of subsequent text. + **********************************************************************/ + +void WINFD::Character_height( /*set text height */ + float height /*height to use */ + ) { + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = CHARHEIGHT; /*send the operator */ + newop->param.p.f = height; /*set parameter */ + } +} + + +/********************************************************************** + * line_type + * + * Set the line type for all subsequent lines. + **********************************************************************/ + +void WINFD::Line_type( /*set line type */ + INT16 style /*style to use */ + ) { + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = LINETYPE; /*send the operator */ + newop->param.p.i = style; /*set parameter */ + } +} + + +/********************************************************************** + * marker_type + * + * Set the marker type for all subsequent lines. + **********************************************************************/ + +void WINFD::Marker_type( /*set marker type */ + INT16 type /*type to use */ + ) { + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = MARKERTYPE; /*send the operator */ + newop->param.p.i = type; /*set parameter */ + } +} + + +/********************************************************************** + * interior_style + * + * Set the fill type and boundary presence for arcs, ellipses etc. + **********************************************************************/ + +void WINFD::Interior_style( /*set polygon style */ + INT16 style, /*style to use */ + INT16 edged /*draw edge or not */ + ) { + TWOOP *newop; /*message structure */ + + /*get some space */ + newop = (TWOOP *) getshm (sizeof (TWOOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = INTERIORSTYLE; /*send the operator */ + newop->param.p[0].i = style; /*set parameter */ + newop->param.p[1].i = edged; + } +} + + +/********************************************************************** + * marker_size + * + * Set the size of markers in polymarker2d. + **********************************************************************/ + +void WINFD::Marker_size( /*set marker size */ + float size /*size to use */ + ) { + TWOOP *newop; /*message structure */ + + /*get some space */ + newop = (TWOOP *) getshm (sizeof (TWOOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = MARKERSIZE; /*send the operator */ + newop->param.p[0].f = size; /*set parameter */ + newop->param.p[1].i = FALSE; + } +} + + +/********************************************************************** + * move2d + * + * Move the pen to the given position. + **********************************************************************/ + +void WINFD::Move2d( /*move the pen */ + float x, /*coords to move to */ + float y /*coords to move to */ + ) { + TWOOP *newop; /*message structure */ + + /*get some space */ + newop = (TWOOP *) getshm (sizeof (TWOOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = MOVE2D; /*send the operator */ + newop->param.p[0].f = x; /*set parameters */ + newop->param.p[1].f = y; + } +} + + +/********************************************************************** + * draw2d + * + * Draw from current position to the given position using the current colour + **********************************************************************/ + +void WINFD::Draw2d( /*draw the pen */ + float x, /*coords to draw to */ + float y /*coords to draw to */ + ) { + TWOOP *newop; /*message structure */ + + /*get some space */ + newop = (TWOOP *) getshm (sizeof (TWOOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = DRAW2D; /*send the operator */ + newop->param.p[0].f = x; /*set parameters */ + newop->param.p[1].f = y; + } +} + + +/********************************************************************** + * rectangle + * + * Draw a rectangle using current perimeter/interior controls. + **********************************************************************/ + +void WINFD::Rectangle( /*draw a rectangle */ + float x1, /*coords to draw to */ + float y1, /*coords to draw to */ + float x2, /*coords to draw to */ + float y2 /*coords to draw to */ + ) { + FOUROP *newop; /*message structure */ + + /*get some space */ + newop = (FOUROP *) getshm (sizeof (FOUROP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = RECTANGLE; /*send the operator */ + newop->param.p[0].f = x1; /*set parameters */ + newop->param.p[1].f = y1; + newop->param.p[2].f = x2; + newop->param.p[3].f = y2; + } +} + + +/********************************************************************** + * text_alignment + * + * Control text position. + **********************************************************************/ + +void WINFD::Text_alignment( /*draw a rectangle */ + INT32 h_select, //horizontal + INT32 v_select, //vertical + float horiz, /*coords to draw to */ + float vert /*coords to draw to */ + ) { + FOUROP *newop; /*message structure */ + + /*get some space */ + newop = (FOUROP *) getshm (sizeof (FOUROP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = TEXT_ALIGNMENT;/*send the operator */ + newop->param.p[0].i = h_select; + newop->param.p[1].i = v_select; + newop->param.p[2].f = horiz; + newop->param.p[3].f = vert; + } +} + + +/********************************************************************** + * polyline2d + * + * Draw a polyline using current line colour/type. + **********************************************************************/ + +void +WINFD::Polyline2d ( /*draw a polyline */ +float clist[], /*coordinate list */ +INT16 numpts, /*number of coords */ +INT16 flags /*does it have move/draws */ +) { + POLYOP *newop; /*message structure */ + INT32 floatcount; /*no of floats */ + + floatcount = flags ? numpts * 3/*move/draw flags in */ + : numpts * 2; /*no move/draw flags */ + /*get some space */ + newop = (POLYOP *) getshm (sizeof (POLYOP) + sizeof (float) * (floatcount - 1)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = POLYLINE2D; /*send the operator */ + /*pointer to array */ + newop->param.clist = newop->clist; + newop->param.numpts = numpts;/*other params */ + newop->param.flags = flags; + memcpy (newop->clist, clist, (UINT32) floatcount * sizeof (float)); + } +} + + +/********************************************************************** + * polygon2d + * + * Draw a polygon using current line colour/type. + **********************************************************************/ + +void +WINFD::Polygon2d ( /*draw a polygon */ +float clist[], /*coordinate list */ +INT16 numpts, /*number of coords */ +INT16 flags /*does it have move/draws */ +) { + POLYOP *newop; /*message structure */ + INT32 floatcount; /*no of floats */ + + floatcount = flags ? numpts * 3/*move/draw flags in */ + : numpts * 2; /*no move/draw flags */ + /*get some space */ + newop = (POLYOP *) getshm (sizeof (POLYOP) + sizeof (float) * (floatcount - 1)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = POLYGON2D; /*send the operator */ + /*pointer to array */ + newop->param.clist = newop->clist; + newop->param.numpts = numpts;/*other params */ + newop->param.flags = flags; + memcpy (newop->clist, clist, (UINT32) floatcount * sizeof (float)); + } +} + + +/********************************************************************** + * polymarker2d + * + * Draw a polymarker using current marker colour/type. + **********************************************************************/ + +void +WINFD::Polymarker2d ( /*draw a polymarker */ +float clist[], /*coordinate list */ +INT16 numpts, /*number of coords */ +INT16 flags /*does it have move/draws */ +) { + POLYOP *newop; /*message structure */ + INT32 floatcount; /*no of floats */ + + floatcount = flags ? numpts * 3/*move/draw flags in */ + : numpts * 2; /*no move/draw flags */ + /*get some space */ + newop = (POLYOP *) getshm (sizeof (POLYOP) + sizeof (float) * (floatcount - 1)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = POLYMARKER2D; /*send the operator */ + /*pointer to array */ + newop->param.clist = newop->clist; + newop->param.numpts = numpts;/*other params */ + newop->param.flags = flags; + memcpy (newop->clist, clist, (UINT32) floatcount * sizeof (float)); + } +} + + +/********************************************************************** + * text2d + * + * Draw a text string using current font, colour, height. + **********************************************************************/ + +void WINFD::Text2d( /*draw a text */ + float x, /*coords of text */ + float y, + const char *string, /*text to draw */ + INT16 xform, /*transform */ + INT16 more /*more text? */ + ) { + TEXTOP *newop; /*message structure */ + INT16 length; /*length of string */ + + length = strlen (string) + 1; /*include null */ + length += 3; + length &= ~3; /*round up to words */ + /*get some space */ + newop = (TEXTOP *) getshm (sizeof (TEXTOP) + length - 4); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = TEXT2D; /*send the operator */ + newop->param.x = x; /*copy parameters */ + newop->param.y = y; + newop->param.string = newop->chars; + newop->param.xform = xform; + newop->param.more = more; + /*copy the string */ + strcpy (newop->chars, string); + } +} + + +/********************************************************************** + * append_text + * + * Draw a text string using current font, colour, height. + **********************************************************************/ + +void WINFD::Append_text( /*draw a text */ + const char *string, /*text to draw */ + INT16 xform, /*transform */ + INT16 more /*more text? */ + ) { + APPENDOP *newop; /*message structure */ + INT16 length; /*length of string */ + + length = strlen (string) + 1; /*include null */ + length += 3; + length &= ~3; /*round up to words */ + /*get some space */ + newop = (APPENDOP *) getshm (sizeof (APPENDOP) + length - 4); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = APPENDTEXT; /*send the operator */ + newop->param.string = newop->chars; + newop->param.xform = xform; + newop->param.more = more; + /*copy the string */ + strcpy (newop->chars, string); + } +} + + +/********************************************************************** + * ellipse + * + * Draw an ellipse using current perimeter/interior controls. + **********************************************************************/ + +void WINFD::Ellipse( /*draw a ellipse */ + float x_radius, /*radii of ellipse */ + float y_radius, /*radii of ellipse */ + float x_center, /*centre of ellipse */ + float y_center, /*centre of ellipse */ + float rotation /*rotation of ellipse */ + ) { + EIGHTOP *newop; /*message structure */ + + /*get some space */ + newop = (EIGHTOP *) getshm (sizeof (EIGHTOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = ELLIPSE; /*send the operator */ + /*set parameters */ + newop->param.p[0].f = x_radius; + newop->param.p[1].f = y_radius; + newop->param.p[2].f = x_center; + newop->param.p[3].f = y_center; + newop->param.p[4].f = rotation; + } +} + + +/********************************************************************** + * arc + * + * Draw an arc using current perimeter/interior controls. + **********************************************************************/ + +void WINFD::Arc( /*draw a arc */ + float x_radius, /*radii of arc */ + float y_radius, /*radii of arc */ + float x_center, /*centre of arc */ + float y_center, /*centre of arc */ + float start, /*ends of arc */ + float stop, /*ends of arc */ + float rotation, /*rotation of arc */ + INT16 close_type /*type of closure */ + ) { + EIGHTOP *newop; /*message structure */ + + /*get some space */ + newop = (EIGHTOP *) getshm (sizeof (EIGHTOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = ARC; /*send the operator */ + /*set parameters */ + newop->param.p[0].f = x_radius; + newop->param.p[1].f = y_radius; + newop->param.p[2].f = x_center; + newop->param.p[3].f = y_center; + newop->param.p[4].f = start; + newop->param.p[5].f = stop; + newop->param.p[6].f = rotation; + newop->param.p[7].i = close_type; + } +} + + +/********************************************************************** + * create_window + * + * Create a window and register event handlers on the window. + * The return "file descriptor" is used in subsequent starbase + * primitives to refer to this window. + **********************************************************************/ + +WINDOW WINFD::create( /*create a window */ + const char *name, /*name/title of window */ + INT8 window_type, /*type of window */ + INT16 xpos, /*coords of window */ + INT16 ypos, /*coords of window */ + INT16 xsize, /*size of window */ + INT16 ysize, /*size of window */ + float xmin, /*scrolling limits */ + float xmax, /*to stop users */ + float ymin, /*getting lost in */ + float ymax, /*empty space */ + BOOL8 downon, /*Events wanted */ + BOOL8 moveon, + BOOL8 upon, + BOOL8 keyon) { + INT16 fd; //integer index + CREATEOP *newop; /*create structure */ + WINDOW win; //output + + if (xmin == xmax || ymin == ymax) + return NO_WINDOW; + if (maxsbfd == 0) { + maxsbfd = 1; /*don't use 0 */ + start_sbdaemon(); /*startup daemon */ + } + + /*find a free one */ + for (fd = 1; fd < maxsbfd && sbfds[fd].used; fd++); + if (fd == maxsbfd) { /*need a new one */ + if (maxsbfd == MAXWINDOWS) + return NO_WINDOW; + maxsbfd++; + } + win = &sbfds[fd]; //this + win->fd = fd; + win->used = TRUE; /*it is in use */ + win->downevent = downon; + win->moveevent = moveon; + win->upevent = upon; + win->keyevent = keyon; + win->click_handler = NULL; + win->selection_handler = NULL; + win->key_handler = NULL; + win->destroy_handler = NULL; + win->events = NULL; + win->lastevent = NULL; + + newop = (CREATEOP *) getshm (sizeof (CREATEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*file descriptor */ + newop->type = CREATE; /*set operator type */ + newop->window_type = window_type; + /*copy window name */ + strncpy (newop->name, name, MAXWINDOWNAME - 1); + newop->name[MAXWINDOWNAME - 1] = '\0'; + newop->xpos = xpos; + newop->ypos = ypos; + newop->xsize = xsize; + newop->ysize = ysize; + newop->xmin = xmin; + newop->xmax = xmax; + newop->ymin = ymin; + newop->ymax = ymax; + newop->downon = downon; + newop->moveon = moveon; + newop->upon = upon; + newop->keyon = keyon; + } + return win; /*file descriptor */ +} + + +/********************************************************************** + * WINFD::WINFD + * + * Constructor to initialize a WINFD entry. + **********************************************************************/ + +WINFD::WINFD() { //constructor + fd = -1; + used = FALSE; + downevent = FALSE; + moveevent = FALSE; + upevent = FALSE; + keyevent = FALSE; + click_handler = NULL; + selection_handler = NULL; + key_handler = NULL; + destroy_handler = NULL; + events = NULL; + lastevent = NULL; +} + + +WINFD::~WINFD () { +} + + +/********************************************************************** + * destroy_window + * + * Destroy a window and free the file descriptor + **********************************************************************/ + +void WINFD::Destroy_window() { /*destroy a window */ + ONEOP *newop; /*destroy structure */ + + if (fd < 1 || fd > maxsbfd || sbfds[fd].used == FALSE) { + return; + } + else { + Clear_event_queue(); + sbfds[fd].used = FALSE; /*it is not in use */ + sbfds[fd].click_handler = NULL; + + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*file descriptor */ + newop->type = DESTROY; /*set operator type */ + } + } +} + + +/********************************************************************** + * Clear_event_queue + * + * Clear the queue of events for this window. + **********************************************************************/ + +void WINFD::Clear_event_queue() { /*clear events */ + INT16 fd; //current window + GRAPHICS_EVENT *event; /*current event */ + GRAPHICS_EVENT *nextevent; /*next in list */ + + if (this == NULL) { + for (fd = 1; fd < maxsbfd; fd++) { + if (sbfds[fd].used) { + sbfds[fd].Clear_event_queue (); + } + } + } + else { + for (event = events; event != NULL; event = nextevent) { + nextevent = event->next; + delete event; //free them all + } + events = NULL; /*there are none now */ + } +} + + +/********************************************************************** + * clear_view_surface + * + * Clear the window and empty the display list, discarding images also. + **********************************************************************/ + +void WINFD::Clear_view_surface() { /*clear window */ + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = CLEAR; /*send the operator */ + } +} + + +/********************************************************************** + * re_compute_colourmap + * + * Tell SBD to recalc_colourmap for this window. + **********************************************************************/ + +void WINFD::Re_compute_colourmap() { /*Mark need to recalc */ + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = RE_COMP_COLMAP;/*send the operator */ + } + /* + ONE DAY THE PC VERSION WILL SUPPORT COLOUR - BUT NOT TODAY + + Among the things that will need doing is to change the size of.. + PCSTUBSPEC stubspecs[SYNCWIN+1]; + in pcsbdg.[ch] to RE_COMP_COLMAP+1 + */ +} + + +/********************************************************************** + * vdc_extent + * + * Shift/scale the window to focus on the given region. + **********************************************************************/ + +void WINFD::Vdc_extent( /*set window focus */ + float Xmin, /*min values */ + float Ymin, /*min values */ + float Xmax, /*max values */ + float Ymax /*max values */ + ) { + EIGHTOP *newop; /*message structure */ + + /*get some space */ + newop = (EIGHTOP *) getshm (sizeof (EIGHTOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = VDCEXTENT; /*send the operator */ + newop->param.p[0].f = Xmin; /*set parameters */ + newop->param.p[1].f = Ymin; + newop->param.p[2].f = 0.0f; + newop->param.p[3].f = Xmax; + newop->param.p[4].f = Ymax; + newop->param.p[5].f = 0.0f; + } +} + + +/********************************************************************** + * set_echo + * + * Set the starbase echo/cursor. + **********************************************************************/ + +void WINFD::Set_echo( /*set window echo */ + ECHO_TYPE echo_type, //type of echo + float xorig, /*min values */ + float yorig /*min values */ + ) { + FOUROP *newop; /*message structure */ + + /*get some space */ + newop = (FOUROP *) getshm (sizeof (FOUROP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = SETECHO; /*send the operator */ + newop->param.p[0].i = echo_type; + newop->param.p[1].f = xorig; + newop->param.p[2].f = yorig; + } +} + + +/********************************************************************** + * overlap_picture_ops + * + * Clear the output buffer without waiting for acknowledge response. + * If update is TRUE, make_picture_currents are issued on all windows, + * but they are still not waited for. + **********************************************************************/ + +DLLSYM void def_overlap_picture_ops( /*flush output */ + BOOL8 update /*send make_ */ + ) { + ONEOP *newop; /*message structure */ + INT16 fd; /*file descriptor */ + + if (update) { + for (fd = 1; fd < maxsbfd; fd++) { + if (sbfds[fd].used) { + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + /*send the operator */ + newop->type = MAKECURRENT; + } + } + } + } + kick_daemon(FLUSH_OUT); /*empty shm */ +} + + +/********************************************************************** + * make_picture_current + * + * Clear the buffer and make sure the image is up-to-date. + * If a window of 0 is given, all windows are updated. + **********************************************************************/ + +void WINFD::Make_picture_current() { /*update window */ + ONEOP *newop; /*message structure */ + + if (this == NULL || fd <= 0) { + overlap_picture_ops(TRUE); + } + else { + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = MAKECURRENT; /*send the operator */ + kick_daemon(FLUSH_IN); /*empty shm */ + } + } +} + + +/********************************************************************** + * synchronize_windows + * + * Make zoom, scroll and resize operations ripple over other window(s). + **********************************************************************/ + +void WINFD::Synchronize_windows( /*set line colour */ + WINDOW fd2 //other window + ) { + ONEOP *newop; /*message structure */ + + /*get some space */ + newop = (ONEOP *) getshm (sizeof (ONEOP)); + if (newop != NULL) { + newop->header.fd = fd; /*send the fd */ + newop->type = SYNCWIN; /*send the operator */ + newop->param.p.i = fd2->fd; /*set parameter */ + } +} + + +/********************************************************************** + * set_click_handler + * + * Set a callback function for click events. + **********************************************************************/ + +void WINFD::Set_click_handler( //set callback function + EVENT_HANDLER handler //handler function + ) { + click_handler = handler; //remember it +} + + +/********************************************************************** + * set_selection_handler + * + * Set a callback function for selection events. + **********************************************************************/ + +void WINFD::Set_selection_handler( //set callback function + EVENT_HANDLER handler //handler function + ) { + selection_handler = handler; //remember it +} + + +/********************************************************************** + * set_key_handler + * + * Set a callback function for key events. + **********************************************************************/ + +void WINFD::Set_key_handler( //set callback function + EVENT_HANDLER handler //handler function + ) { + key_handler = handler; //remember it +} + + +/********************************************************************** + * set_destroy_handler + * + * Set a callback function for destroy events. + **********************************************************************/ + +void WINFD::Set_destroy_handler( //set callback function + EVENT_HANDLER handler //handler function + ) { + destroy_handler = handler; //remember it +} diff --git a/viewer/grphics.h b/viewer/grphics.h new file mode 100644 index 0000000000..5226668b5a --- /dev/null +++ b/viewer/grphics.h @@ -0,0 +1,268 @@ +/********************************************************************** + * File: grphics.h (Formerly graphics.h) + * Description: Starbase stubs for connection to sbdaemon + * Author: Ray Smith + * Created: Wed May 16 08:34:32 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef GRPHICS_H +#define GRPHICS_H + +//This is the main include file needed to get sbdaemon functionality +//TO BUILD A PROGRAM THAT USES SBDAEMON, YOU NEED: +//GRPHICS.CPP GRPHSHM.CPP AND EVNTLST.CPP. +//If you want to be able to wait for events, add evnts.cpp and include evnts.h. +//If you want to be able to show images, add showim.cpp and include showim.h. + +#include "sbgdefs.h" +#include "sbgconst.h" + +#define STATESIZE 13 //MUST equal EDGESTYLE+1 + +#define line_color_index(fd,index) fd->Line_color_index(index) +#define perimeter_color_index(fd,index) fd->Perimeter_color_index(index) +#define fill_color_index(fd,index) fd->Fill_color_index(index) +#define fill_color(fd,r,g,b) fd->Fill_color(r,g,b) +#define text_color_index(fd,index) fd->Text_color_index(index) +#define text_font_index(fd,index) fd->Text_font_index(index) +#define character_height(fd,height) fd->Character_height(height) +#define line_type(fd,style) fd->Line_type(style) +#define interior_style(fd,style,edged) fd->Interior_style(style,edged) +#define move2d(fd,x,y) fd->Move2d(x,y) +#define draw2d(fd,x,y) fd->Draw2d(x,y) +#define rectangle(fd,x1,y1,x2,y2) fd->Rectangle(x1,y1,x2,y2) +#define text2d(fd,x,y,string,xform,more) fd->Text2d(x,y,string,xform,more) +#define ellipse(fd,x_radius,y_radius,x_center,y_center,rotation) fd->Ellipse(x_radius,y_radius,x_center,y_center,rotation) +#define destroy_window(fd) fd->Destroy_window() +#define clear_view_surface(fd) fd->Clear_view_surface() +#define vdc_extent(fd,xmin,ymin,xmax,ymax) fd->Vdc_extent(xmin,ymin,xmax,ymax) +#define make_picture_current(fd) fd->Make_picture_current() +#define set_click_handler(fd,handler) fd->Set_click_handler(handler) +#define set_selection_handler(fd,handler) fd->Set_selection_handler(handler) +#define set_key_handler(fd,handler) fd->Set_key_handler(handler) +#define set_destroy_handler(fd,handler) fd->Set_destroy_handler(handler) +#define clear_event_queue(fd) fd->Clear_event_queue() +#define create_window(name,window_type,xpos,ypos,xsize,ysize,xmin,xmax,ymin,ymax,downon,moveon,upon,keyon) (*create_func)(name,window_type,xpos,ypos,xsize,ysize,xmin,xmax,ymin,ymax,downon,moveon,upon,keyon) +#define overlap_picture_ops(update) (*overlap_func)(update) +#define await_event(win,wait,type,event) (*await_event_func)(win,wait,type,event) + +typedef void (*EVENT_HANDLER) (GRAPHICS_EVENT *); + /*name/title of window */ +typedef WINDOW (*WINCREATEFUNC) (const char *name, +INT8 window_type, /*type of window */ +INT16 xpos, /*coords of window */ +INT16 ypos, /*coords of window */ +INT16 xsize, /*size of window */ +INT16 ysize, /*size of window */ +float xmin, /*scrolling limits */ +float xmax, /*to stop users */ +float ymin, /*getting lost in */ +float ymax, /*empty space */ +BOOL8 downon, /*Events wanted */ +BOOL8 moveon, BOOL8 upon, BOOL8 keyon); + +extern WINCREATEFUNC create_func; +extern void (*overlap_func) (BOOL8); +extern WINDOW (*await_event_func) (WINDOW, BOOL8, INT8, GRAPHICS_EVENT *); + +class DLLSYM WINFD +{ + public: + //Constructors for WINFD are host dependent to match different + //implementations and are intended to be called only by + //create_window. Use create_window to make a window. + static WINDOW create( /*create a window */ + const char *name, /*name/title of window */ + INT8 window_type, /*type of window */ + INT16 xpos, /*coords of window */ + INT16 ypos, /*coords of window */ + INT16 xsize, /*size of window */ + INT16 ysize, /*size of window */ + float xmin, /*scrolling limits */ + float xmax, /*to stop users */ + float ymin, /*getting lost in */ + float ymax, /*empty space */ + BOOL8 downon, /*Events wanted */ + BOOL8 moveon, + BOOL8 upon, + BOOL8 keyon); + WINFD(); //constructor + virtual ~ WINFD (); + + /*set line colour */ + virtual void Line_color_index(COLOUR index); /*index to use */ + /*set perimeter colour */ + virtual void Perimeter_color_index(COLOUR index); /*index to use */ + /*set fill colour */ + virtual void Fill_color_index(COLOUR index); /*index to use */ + virtual void Fill_color( /*set RGB fill colour */ + UINT8 red, + UINT8 green, + UINT8 blue); + /*set marker colour */ + virtual void Marker_color_index(COLOUR index); /*index to use */ + /*set text colour */ + virtual void Text_color_index(COLOUR index); /*index to use */ + /*set text font */ + virtual void Text_font_index(INT16 index); /*index to use */ + /*set text height */ + virtual void Character_height(float height); /*height to use */ + virtual void Line_type( /*set line type */ + INT16 style); /*style to use */ + virtual void Marker_type( /*set marker type */ + INT16 type); /*type to use */ + virtual void Interior_style( /*set polygon style */ + INT16 style, /*style to use */ + INT16 edged); /*draw edge or not */ + virtual void Marker_size( /*set marker size */ + float size); /*size to use */ + virtual void Move2d( /*move the pen */ + float x, /*coords to move to */ + float y); /*coords to move to */ + virtual void Draw2d( /*draw the pen */ + float x, /*coords to draw to */ + float y); /*coords to draw to */ + virtual void Rectangle( /*draw a rectangle */ + float x1, /*coords to draw to */ + float y1, /*coords to draw to */ + float x2, /*coords to draw to */ + float y2); /*coords to draw to */ + virtual void Text_alignment( /*draw a rectangle */ + INT32 h_select, //horizontal + INT32 v_select, //vertical + float horiz, /*coords to draw to */ + float vert); /*coords to draw to */ + virtual void Polyline2d ( /*draw a polyline */ + float clist[], /*coordinate list */ + INT16 numpts, /*number of coords */ + INT16 flags); /*does it have move/draws */ + virtual void Polygon2d ( /*draw a polygon */ + float clist[], /*coordinate list */ + INT16 numpts, /*number of coords */ + INT16 flags); /*does it have move/draws */ + virtual void Polymarker2d ( /*draw a polymarker */ + float clist[], /*coordinate list */ + INT16 numpts, /*number of coords */ + INT16 flags); /*does it have move/draws */ + virtual void Text2d( /*draw a text */ + float x, /*coords of text */ + float y, + const char *string, /*text to draw */ + INT16 xform, /*transform */ + INT16 more); /*more text? */ + void Append_text( /*draw a text */ + const char *string, /*text to draw */ + INT16 xform, /*transform */ + INT16 more); /*more text? */ + virtual void Ellipse( /*draw a ellipse */ + float x_radius, /*radii of ellipse */ + float y_radius, /*radii of ellipse */ + float x_center, /*centre of ellipse */ + float y_center, /*centre of ellipse */ + float rotation); /*rotation of ellipse */ + virtual void Arc( /*draw a arc */ + float x_radius, /*radii of arc */ + float y_radius, /*radii of arc */ + float x_center, /*centre of arc */ + float y_center, /*centre of arc */ + float start, /*ends of arc */ + float stop, /*ends of arc */ + float rotation, /*rotation of arc */ + INT16 close_type); /*type of closure */ + /*destroy a window */ + virtual void Destroy_window(); + /*clear window */ + virtual void Clear_view_surface(); + /*Mark need to recalc */ + virtual void Re_compute_colourmap(); + virtual void Vdc_extent( /*set window focus */ + float xmin, /*min values */ + float ymin, /*min values */ + float xmax, /*max values */ + float ymax); /*max values */ + void Set_echo( /*set window echo */ + ECHO_TYPE echo_type, //type of echo + float xorig, /*min values */ + float yorig); /*min values */ + /*update window */ + virtual void Make_picture_current(); + /*flush output */ + friend void def_overlap_picture_ops(BOOL8 update); /*send make_ */ + /*set line colour */ + virtual void Synchronize_windows(WINDOW fd2); //other window + /* void Show_sub_image( //show rectangle + IMAGE* source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + INT32 xdest, //destination coords + INT32 ydest);*/ + + INT16 get_fd() { //access + return fd; + } + + void Set_selection_handler( //set callback function + EVENT_HANDLER handler); //handler function + void Set_key_handler( //set callback function + EVENT_HANDLER handler); //handler function + void Set_destroy_handler( //set callback function + EVENT_HANDLER handler); //handler function + void Set_click_handler( //set callback function + EVENT_HANDLER handler); //handler function + /*delete all events */ + virtual void Clear_event_queue(); + + //internal maintenance functions + friend void add_event( /*add an event */ + GRAPHICS_EVENT *event); /*event to add */ + /*search for event */ + friend GRAPHICS_EVENT *search_event_queue(INT16 &fd, /*queue to search */ + INT8 event_type); /*type to search for */ + /*search for event */ + friend GRAPHICS_EVENT *search_single_queue(INT16 fd, /*queue to search */ + INT8 event_type); /*type to search for */ + + protected: + EVENT_HANDLER click_handler; //callback function + //callback function + EVENT_HANDLER selection_handler; + EVENT_HANDLER key_handler; //callback function + //callback function + EVENT_HANDLER destroy_handler; + private: + void get_lock() { + } //wait for lock + void get_lock_for_draw() { + } //kill draw thread + void release_lock() { + } //let it go + void get_core_lock() { + } //wait for lock + void release_core_lock() { + } //let it go + + INT16 fd; //"file descriptor" + BOOL8 used; /*true if fd in use */ + BOOL8 downevent; /*event flags */ + BOOL8 moveevent; + BOOL8 upevent; + BOOL8 keyevent; + GRAPHICS_EVENT *events; /*event queue */ + GRAPHICS_EVENT *lastevent; /*event queue */ + +}; +#endif diff --git a/viewer/grphshm.cpp b/viewer/grphshm.cpp new file mode 100644 index 0000000000..f9264cd07f --- /dev/null +++ b/viewer/grphshm.cpp @@ -0,0 +1,472 @@ +/********************************************************************** + * File: grphshm.c (Formerly graphshm.c) + * Description: Functions for the shared memory sbdaemon connection. + * Author: Ray Smith + * Created: Thu May 24 14:09:38 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "evntlst.h" +#ifdef __UNIX__ +#include +#include +//#include +#include +//#include "pipes.h" +#include "fileerr.h" +//#include "grpherr.h" +//#include "basefile.h" +#include +#elif defined(__MSW32__) +#include +#include +#include +#include +#else +#endif +#include +#include "grphics.h" +#include "grphshm.h" + +#define EXTERN + +EXTERN SHMINFO shminfo; /*shared memory */ +EXTERN WINFD sbfds[MAXWINDOWS]; /*file descriptors */ +EXTERN INT16 maxsbfd = 0; /*no of fds in use */ +#ifdef __MSW32__ + //event thread id +EXTERN DWORD event_id = (DWORD) - 1; +#endif + +/********************************************************************** + * start_sbdaemon + * + * Creates the shared memory segment and starts up the sbdaemon telling + * it info about the segment. This only needs to be called once. + * It is called automatically from create_window at the first creation attempt. + **********************************************************************/ + +void start_sbdaemon() { /*start the daemon */ + #if defined (__UNIX__) || defined(__MSW32__) + char *shmaddr; /*address to attach */ + char *sbenv; /*SB_DISPLAY_ADDR */ + INT32 sbaddr; /*starbase address */ + INT32 wmshm; //windows shared mem + char arg1[MAX_PATH]; /*shmid argument */ + char arg2[MAX_PATH]; /*shmstart argument */ + char arg3[MAX_PATH]; /*shmsize argument */ + const char *argv[5]; /*args of sbdaemon */ + /*for pipe usage */ + static char pipebuffer[PIPESIZE]; + + sbenv = getenv (SBADDR); /*get SB_DISPLAY_ADDR */ + if (sbenv == NULL || sscanf (sbenv, INT32FORMAT, &sbaddr) != 1) + sbaddr = SBDEFAULT; + shmaddr = getenv (WMSHM); + if (shmaddr == NULL || sscanf (shmaddr, INT32FORMAT, &wmshm) != 1) + wmshm = WMSHMDEFAULT; + /*default address */ + shmaddr = (char *) sbaddr - (wmshm + SHMSIZE + SHMOFFSET); + + if (!remote_display (arg1)) { + shminfo.shmsize = SHMSIZE; /*size of segment */ + #ifdef __UNIX__ + shminfo.shmid = shmget (IPC_PRIVATE, SHMSIZE, USER_RW); + /*get shm segment */ + // if (shminfo.shmid==-1) + // NO_SHM_SEGMENT.error("start_sbdaemon",ABORT,"Errno=%d",errno); + #ifdef hp9000s800 + /*attach it */ + shminfo.shmstart = shmat (shminfo.shmid, 0, 0); + if ((int) shminfo.shmstart == -1) + #else + /*attach it */ + shminfo.shmstart = shmat (shminfo.shmid, shmaddr, 0); + // if (shminfo.shmstart!=shmaddr) + #endif + // SHM_ATTACH_FAILED.error("start_sbdaemon",ABORT,"Errno=%d",errno); + #else + SECURITY_ATTRIBUTES security;//for handles + + security.nLength = sizeof (security); + security.lpSecurityDescriptor = NULL; + //make it inheritable + security.bInheritHandle = TRUE; + //anonymous + shminfo.shmid = CreateFileMapping ((HANDLE) 0xffffffff, &security, PAGE_READWRITE, 0, shminfo.shmsize + 3 * sizeof (INT32) + EVENTSIZE * sizeof (SBD_GRAPHICS_EVENT), NULL); + if (shminfo.shmid == NULL) { + shminfo.shmstart = NULL; + return; //quietly fail + } + shminfo.shmstart = + MapViewOfFile (shminfo.shmid, FILE_MAP_WRITE, 0, 0, 0); + if (shminfo.shmstart == NULL) + return; + EVENT_TAIL = 0; + EVENT_HEAD = 0; + #endif + /*set up args */ + sprintf (arg1, "%d", shminfo.shmid); + sprintf (arg2, "%p", shminfo.shmstart); + sprintf (arg3, INT32FORMAT, shminfo.shmsize); + argv[0] = SBDAEMON; /*set up argv */ + argv[1] = arg1; + argv[2] = arg2; + argv[3] = arg3; + argv[4] = NULL; + } + else { + shmaddr = NULL; //remote + fprintf (stderr, "start_sbdaemon:using %s to connect to machine %s\n", + REMSH, arg1); + #ifdef __UNIX__ + shminfo.shmid = -1; + #else + shminfo.shmid = NULL; + #endif + /*not using shm */ + shminfo.shmstart = pipebuffer; + shminfo.shmsize = PIPESIZE; /*size of pipe buffer */ + #ifdef __UNIX__ + /*command on host */ + sprintf (arg2, "%s=0x%x; export %s; %s -1 0 " INT32FORMAT " %s", SBADDR, sbaddr, SBADDR, SBDAEMON, shminfo.shmsize, getenv (DISP)); + #else + /*command on host */ + sprintf (arg2, "%s -1 0 %d %s", SBDAEMON, shminfo.shmsize, getenv (DISP)); + #endif + argv[0] = REMSH; /*set up argv */ + argv[1] = arg1; /*host to use */ + argv[2] = arg2; + argv[3] = NULL; + } + + shminfo.usedsize = 0; /*none used yet */ + + #ifdef __UNIX__ + // shminfo.pid=two_way_pipe(argv[0],argv,shminfo.fds); /*start daemon*/ + #else + if (two_way_pipe (argv[0], argv, shminfo.fds) != 0) { + cleanup_sbdaemon(); + } + else { + //anonymous + event_sem = CreateSemaphore (NULL, 1, 1, NULL); + //xiaofan + _beginthread (event_reader, 0, &shminfo.fds[INFD]); + } + #endif + #endif +} + + +/********************************************************************** + * cleanup_sbdaemon + * + * Free system resources for when the daemon has failed or been killed. + **********************************************************************/ +void cleanup_sbdaemon() { /*forget about the daemon */ + #ifdef __MSW32__ + if (shminfo.fds[INFD] != NULL) { + CloseHandle (shminfo.fds[INFD]); + shminfo.fds[INFD] = 0; + } + if (shminfo.fds[OUTFD] != NULL) { + CloseHandle (shminfo.fds[OUTFD]); + shminfo.fds[OUTFD] = 0; + } + if (shminfo.shmstart != NULL) { + UnmapViewOfFile (shminfo.shmstart); + shminfo.shmstart = NULL; + } + if (shminfo.shmid != NULL) { + CloseHandle (shminfo.shmid); + shminfo.shmid = NULL; + } + if (event_sem != NULL) { + CloseHandle(event_sem); + event_sem = NULL; + } + #elif defined(__UNIX__) + if (shminfo.fds[INFD] > 0) { + close (shminfo.fds[INFD]); + shminfo.fds[INFD] = 0; + } + if (shminfo.fds[OUTFD] > 0) { + close (shminfo.fds[OUTFD]); + shminfo.fds[OUTFD] = 0; + } + shminfo.shmstart = NULL; + #endif +} + + +/********************************************************************** + * remote_display + * + * Returns TRUE if the DISPLAY environment variable points to a + * Remote display, and sets the name to the name of the host. + * Otherwise, returns FALSE. + **********************************************************************/ + +BOOL8 remote_display( //check for remote + char *name //name of host + ) { + #if defined (__UNIX__) || defined(__MSW32__) + char *xenv; /*DISPLAY environ */ + char *nameend; //end of name + #ifdef __UNIX__ + char thishost[MAX_PATH]; //current host + #endif + + xenv = getenv (DISP); /*get display variable */ + if (xenv != NULL) { + strcpy(name, xenv); + nameend = strchr (name, ':'); + if (nameend != NULL) + *nameend = '\0'; /*chop display off */ + nameend = strchr (name, '.'); + if (nameend != NULL) + *nameend = '\0'; /*chop resolv off */ + #ifdef __UNIX__ + if (strcmp (name, LOCAL1) && strcmp (name, LOCAL2) + && gethostname (thishost, MAX_PATH) >= 0) { + nameend = strchr (thishost, '.'); + if (nameend != NULL) + *nameend = '\0'; /*chop resolv off */ + if (strcmp (name, thishost)) { + return TRUE; + } + } + #else + return TRUE; + #endif + } + #endif + return FALSE; +} + + +/********************************************************************** + * getshm + * + * Get the next element of the shared memory. If there is no more room + * in the segment, kick the daemon to get it to empty it out and then + * restart the buffer once it acknowledges the cleanout. + **********************************************************************/ + +DLLSYM void *getshm( /*get memory */ + INT32 size /*required size */ + ) { + void *segment; /*return segment */ + + if (shminfo.shmstart == NULL) + return NULL; //no daemon connection + size = (size + 3) & ~3; + if (size > shminfo.shmsize) + return NULL; + /*too full? */ + if (shminfo.usedsize + size > shminfo.shmsize + || shminfo.usedsize < 0) { /*or read pending */ + kick_daemon(AWAIT_BUFFER); /*get it to read */ + } + /*address of segment */ + segment = (char *) shminfo.shmstart + shminfo.usedsize; + shminfo.usedsize += size; /*sum used sizes */ + return segment; +} + + +/********************************************************************** + * kick_daemon + * + * Tell the daemon to read the shared memory and perform all the + * operations in it. This function blocks until the daemon has + * emptied the queue. + **********************************************************************/ + +void kick_daemon( /*empty queue */ + INT8 mode /*control mode */ + ) { + #ifndef __MAC__ + SBD_GRAPHICS_EVENT event; /*event from daemon */ + GRAPHICS_EVENT real_event; //converted format + #ifdef __MSW32__ + unsigned long nwrite; + unsigned long nread; //bytes read + char pipe_char[2]; //char from pipe + INT32 pipe_index; //index to event queue + #endif + static INT16 reads_pending = 0;/*acknowledges pending */ + + if (mode == COUNT_READS) { + lock_events(); + reads_pending--; /*got a read */ + unlock_events(); + return; + } + if (shminfo.shmstart == NULL) + return; //no connection + if (shminfo.usedsize > 0) { + #ifdef __UNIX__ + if (write + (shminfo.fds[OUTFD], (const char *) &shminfo.usedsize, + sizeof (INT32)) != sizeof (INT32)) + WRITEFAILED.error ("kick_daemon", EXIT, "sbdaemon pipe"); + #else + PRIMITIVES = shminfo.usedsize; + if (WriteFile (shminfo.fds[OUTFD], "xx", 2, &nwrite, NULL) == 0 + || nwrite != 2) { + cleanup_sbdaemon(); + return; + } + #endif + #ifdef __UNIX__ + if (shminfo.shmid < 0) { + if (write (shminfo.fds[OUTFD], (const char *) shminfo.shmstart, + shminfo.usedsize) != shminfo.usedsize) + WRITEFAILED.error ("kick_daemon", EXIT, "sbdaemon pipe"); + #else + if (shminfo.shmid == NULL) { + if (WriteFile (shminfo.fds[OUTFD], (const char *) shminfo.shmstart, + shminfo.usedsize, &nwrite, NULL) == 0 + || nwrite != (UINT32) shminfo.usedsize) { + cleanup_sbdaemon(); + return; + } + #endif + shminfo.usedsize = 0; /*can use it now */ + } + else + shminfo.usedsize = -1; /*need to wait */ + lock_events(); + reads_pending++; /*acknowledges due */ + unlock_events(); + } + if (mode == FLUSH_IN || reads_pending > MAX_PENDING || mode == AWAIT_BUFFER + #ifdef __UNIX__ + && shminfo.shmid < 0) + #else + && shminfo.shmid != NULL) + #endif + { + while (reads_pending > 0) { + #ifdef __MSW32__ + if (event_id == GetCurrentThreadId ()) { + if (ReadFile (shminfo.fds[INFD], pipe_char, 2, &nread, NULL) != 0 + && nread == 2) { + pipe_index = EVENT_HEAD; + event = EVENT_INDEX (pipe_index); + pipe_index++; + if (pipe_index >= EVENTSIZE) + pipe_index = 0; + EVENT_HEAD = pipe_index; + #endif + #ifdef __UNIX__ + if (read + (shminfo.fds[INFD], &event, + sizeof (SBD_GRAPHICS_EVENT)) != + sizeof (SBD_GRAPHICS_EVENT)) + READFAILED.error ("kick_daemon", EXIT, "sbdaemon pipe"); + #endif + if (event.type != QUEUE_CLEAR) { + real_event.fildes = event.fd; + real_event.type = event.type; + real_event.key = event.key; + real_event.x = event.x; + real_event.y = event.y; + real_event.next = NULL; + /*add event to queue */ + add_event(&real_event); + } + else + reads_pending--; /*got acknowledge */ + #ifdef __MSW32__ + } + } + else + Sleep (50); + #endif + } + if (shminfo.usedsize < 0) //must be reentrant + shminfo.usedsize = 0; /*none used now */ + } + #endif + } + + #ifdef __MSW32__ + /********************************************************************** + * two_way_pipe + * + * Open the process and connect a 2 way pipe to its stdin and stdout. + **********************************************************************/ + + int + two_way_pipe ( //do one file + const char *file, //program to run + const char *argv[], //args to execvp + HANDLE fds[] //output fds + ) { + int argind; //argument index + HANDLE infds[2]; //input fds + HANDLE outfds[2]; //output fds + HANDLE sends[2]; //fds for child + HANDLE process; //current process + STARTUPINFO start_info; //start information + //process info + PROCESS_INFORMATION proc_info; + char cmd[MAX_PATH * 2]; //final command line + + if (CreatePipe (&infds[0], &infds[1], NULL, PIPESIZE) == 0 + || CreatePipe (&outfds[0], &outfds[1], NULL, PIPESIZE) == 0) + return -1; + /* if (_pipe(infds,PIPESIZE,_O_BINARY)<0 + || _pipe(outfds,PIPESIZE,_O_BINARY)<0) + return -1; */ + process = GetCurrentProcess (); + if (DuplicateHandle (process, outfds[0], + process, &sends[0], GENERIC_READ, TRUE, + DUPLICATE_CLOSE_SOURCE) == 0) + return -1; + if (DuplicateHandle (process, infds[1], + process, &sends[1], GENERIC_WRITE, TRUE, + DUPLICATE_CLOSE_SOURCE) == 0) + return -1; + + cmd[0] = '\0'; + for (argind = 0; argv[argind] != NULL; argind++) { + if (argind != 0) + strcat (cmd, " "); + strcat (cmd, argv[argind]); + } + + GetStartupInfo(&start_info); + start_info.wShowWindow = FALSE; + start_info.hStdInput = sends[0]; + start_info.hStdOutput = sends[1]; + start_info.dwFlags = STARTF_USESTDHANDLES; + if (!CreateProcess (NULL, (char *) cmd, NULL, NULL, TRUE, + CREATE_NO_WINDOW | CREATE_SUSPENDED, NULL, NULL, + &start_info, &proc_info)) + return -1; + CloseHandle (sends[0]); + CloseHandle (sends[1]); + CloseHandle (proc_info.hProcess); + ResumeThread (proc_info.hThread); + CloseHandle (proc_info.hThread); + fds[INFD] = infds[0]; + fds[OUTFD] = outfds[1]; + return 0; + } + #endif diff --git a/viewer/grphshm.h b/viewer/grphshm.h new file mode 100644 index 0000000000..9e69fe76a9 --- /dev/null +++ b/viewer/grphshm.h @@ -0,0 +1,74 @@ +/********************************************************************** + * File: grphshm.h (Formerly graphshm.h) + * Description: Header for graphics shared memory functions. + * Author: Ray Smith + * Created: Thu May 24 15:29:28 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef GRPHSHM_H +#define GRPHSHM_H + +#include "sbgtypes.h" +#include "grphics.h" + +#define SHMSIZE (65536*8) /*shm segment size */ +#define PIPESIZE 8192 /*pipe bufer size */ +#define USER_RW 0600 /*permission flags */ + /*starbase environ */ +#define SBADDR "SB_DISPLAY_ADDR" +#define WMSHM "WMSHMSPC" /*shared mem size */ +#define WMSHMDEFAULT 0x200000 /*default value */ +#define SBDEFAULT 0x0b00000 /*default ADDR */ +#define SHMOFFSET 0x200000 /*offset before sbaddr */ +#define MAXDATA 0x1000000 /*default data seg size */ + +#define SBDAEMON "sbdaemon" +#define REMSH "remsh" /*command for remote use */ +#define DISP "DISPLAY" /*environ var */ +#define LOCAL1 "local" /*possible values */ +#define LOCAL2 "unix" /*of DISPLAY */ + +#define FLUSH_OUT 0 /*kick_daemon commands */ +#define FLUSH_IN 1 +#define AWAIT_BUFFER 2 /*wait for free buffer */ +#define COUNT_READS 3 /*count a queue clear */ +#define MAX_PENDING 255 /*max pending reads */ + +extern SHMINFO shminfo; /*shared memory */ +extern WINFD sbfds[MAXWINDOWS]; /*file descriptors */ +extern INT16 maxsbfd; +#ifdef __MSW32__ +extern DWORD event_id; //event thread id +#endif + +void start_sbdaemon(); /*start the daemon */ +void cleanup_sbdaemon(); /*forget about the daemon */ +BOOL8 remote_display( //check for remote + char *name //name of host + ); +DLLSYM void *getshm( /*get memory */ + INT32 size /*required size */ + ); +void kick_daemon( /*empty queue */ + INT8 mode /*control mode */ + ); +#ifdef __MSW32__ +int two_way_pipe ( //do one file +const char *file, //program to run +const char *argv[], //args to execvp +HANDLE fds[] //output fds +); +#endif +#endif diff --git a/viewer/sbgconst.h b/viewer/sbgconst.h new file mode 100644 index 0000000000..95e8c69180 --- /dev/null +++ b/viewer/sbgconst.h @@ -0,0 +1,135 @@ +/********************************************************************** + * File: sbgconst.h (Formerly sbconst.h) + * Description: Header file of constants needed by graphics code. + * Author: Ray Smith + * Created: Thu May 24 14:19:43 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef SBGCONST_H +#define SBGCONST_H + +//This file contains all the symbols and constants that the user sees +//that are not defined by Starbase. + +#include "host.h" + +#define WINDOWNAMESIZE 13 /*max size of name */ +#define MAXWINDOWS 64 /*max allowed windows */ +#define MAXWINDOWNAME 1024 /*max name length */ + +#define NO_WINDOW 0 /*no legal window */ +#define SMDWINDOW 0 /*use smd, no window */ +#define SCROLLINGWIN 1 /*scrolling window */ +#define FULLSIZEWIN 2 /*non-scrolling window */ +#define DCWIN 3 /*dc drawing only */ + +#define M_DOT 0 /*marker_types */ +#define M_PLUS 1 /*starbase markers */ +#define M_ASTERISK 2 +#define M_CIRCLE 3 +#define M_CROSS 4 +#define M_TRIANGLE 5 +#define M_SQUARE 6 +#define M_DIAMOND 7 +#define M_CROSSED_SQUARE 8 + +class WINFD; +typedef WINFD *WINDOW; //compatible with old code + +typedef enum { + BLACK, + WHITE, + RED, + YELLOW, + GREEN, + CYAN, + BLUE, + MAGENTA, + AQUAMARINE, + DARK_SLATE_BLUE, + LIGHT_BLUE, + MEDIUM_BLUE, + MIDNIGHT_BLUE, + NAVY_BLUE, + SKY_BLUE, + SLATE_BLUE, + STEEL_BLUE, + CORAL, + BROWN, + SANDY_BROWN, + GOLD, + GOLDENROD, + DARK_GREEN, + DARK_OLIVE_GREEN, + FOREST_GREEN, + LIME_GREEN, + PALE_GREEN, + YELLOW_GREEN, + LIGHT_GREY, + DARK_SLATE_GREY, + DIM_GREY, + GREY, + KHAKI, + MAROON, + ORANGE, + ORCHID, + PINK, + PLUM, + INDIAN_RED, + ORANGE_RED, + VIOLET_RED, + SALMON, + TAN, + TURQUOISE, + DARK_TURQUOISE, + VIOLET, + WHEAT, + GREEN_YELLOW +} COLOUR; /*starbase colours */ + +enum ECHO_TYPE +{ + NO_ECHO, + BEST_ECHO, + CROSS_HAIR_ECHO, + SMALL_X_ECHO, + RUBBER_LINE_ECHO, + RUBBER_BOX_ECHO, + ALPHA_ECHO +}; + +/*Event types*/ +#define QUEUE_CLEAR 0 /*queue is empty */ +#define DOWN_EVENT 1 /*button press */ +#define MOVE_EVENT 2 /*pointer move */ +#define UP_EVENT 3 /*button release */ +#define KEYPRESS_EVENT 4 /*key pressed */ +#define SELECT_EVENT 5 /*press-release pair */ +#define ANY_EVENT 6 /*any type */ +#define DESTROY_EVENT 7 + +typedef struct graphicsevent +{ + struct graphicsevent *next; /*next event */ + WINDOW fd; //structure of window + INT16 fildes; //unix only + INT8 type; /*event type */ + char key; /*keypress */ + float x, y; /*position of event */ + float xmax, ymax; //for selection +} GRAPHICS_EVENT; /*event type */ + +typedef void (*EVENT_HANDLER) (GRAPHICS_EVENT *); +#endif diff --git a/viewer/sbgdefs.h b/viewer/sbgdefs.h new file mode 100644 index 0000000000..b31a62425c --- /dev/null +++ b/viewer/sbgdefs.h @@ -0,0 +1,421 @@ +/* STARBASE_ID:sb.c.h 286.1 07/06/89 22:09:56 */ + +/* (c) Copyright Hewlett-Packard Company, 1985. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + All rights are reserved. Copying or other + reproduction of this program except for archival + purposes is prohibited without the prior + written consent of Hewlett-Packard Company. + + RESTRICTED RIGHTS LEGEND + + Use, duplication, or disclosure by the U.S. Government + is subject to restrictions as set forth in + subdivision (b) (3) (ii) of the Rights in Technical + Data and Computer Software clause at + 52.227-7013. + + HEWLETT-PACKARD COMPANY + Fort Collins, Colorado */ + +/************************************************************************ + * + * definitions and types to be included with STARBASE application + * programs + * + ***********************************************************************/ + +#ifndef _STARBASE_INCLUDED /* allow multiple inclusions */ +#define _STARBASE_INCLUDED + +/* kinds of graphics devices */ +#define OUTDEV 0 +#define INDEV 1 +#define OUTINDEV 2 +#define OUTMETA 3 +#define INMETA 4 + +/* clear control parameters */ +#define CLEAR_VDC_EXTENT 1 +#define CLEAR_VIEWPORT 17 +#define CLEAR_CLIP_RECTANGLE 33 +#define CLEAR_DISPLAY_SURFACE 65 +#define CLEAR_ALL_BANKS 128 +#define CLEAR_ZBUFFER 256 + +/* clip control parameters */ +#define CLIP_TO_RECT 1 +#define CLIP_TO_VDC 2 +#define CLIP_OFF 3 +#define CLIP_TO_VIEWPORT 4 + +/* gopen open_mode parameter masks */ +#define SPOOLED 0x01 /* if this bit is on, output is spooled */ +//#define INIT 0x02 /* if this bit is on, device initialization occurs */ +#define THREE_D 0x04 /* if this bit is on, all transformations are 3D */ +#define RESET_DEVICE 0x08 /* if this bit is on, hard reset including p1 & p2 */ +#define MODEL_XFORM 0x10 /* if this bit is on, matrix stack in modeling mode */ +#define INT_XFORM 0x20 /* if this bit is on, matrix stack in modeling mode */ +#define FLOAT_XFORM 0x40 /* if this bit is on, matrix stack in modeling mode */ + +/* color map modes set with shade_mode */ +#define CMAP_NORMAL 0 +#define CMAP_MONOTONIC 1 +#define CMAP_FULL 4 + +/* double_buffer mode used to draw into the same buffer that is displayed */ +#define DFRONT 4 +/* double_buffer mode used to not clear buffer when switched */ +#define SUPPRESS_CLEAR 8 + +/* light source types set with light_source */ +#define DIRECTIONAL 0 +#define POSITIONAL 1 +#define ATTEN_LIGHT 2 +#define SPOT_LIGHT 4 +#define CONE_LIGHT 8 + +/* vertex orders set with vertex_format */ +#define CLOCKWISE 0x0000 +#define COUNTER_CLOCKWISE 0x0001 +#define UNIT_NORMALS 0x0200 + +/* set p1 p2 units */ +#define FRACTIONAL 0 +#define METRIC 1 + +/* mapping modes */ +#define ISOTROPIC 0 +#define DISTORT 1 + +/* Starbase linetypes */ +#define SOLID 0 +#define DASH 1 +#define DOT 2 +#define DASH_DOT 3 +#define DASH_DOT_DOT 4 +#define LONG_DASH 5 +#define CENTER_DASH 6 +#define CENTER_DASH_DASH 7 + +/* wide endpoint types */ +#define SQUARE 0 +#define ROUNDED 1 + +/* depth cue models */ +#define DC_MIN 2 +#define DC_COLOR 4 + +/* distance modes for line_width, hatch_spacing */ +#define VDC_UNITS 0 +#define WC_UNITS 1 +#define MC_UNITS WC_UNITS +#define DC_UNITS 2 + +/* polygon interior styles */ +#define INT_HOLLOW 0 +#define INT_SOLID 1 +#define INT_PATTERN 2 +#define INT_HATCH 3 +#define INT_OUTLINE 4 +#define INT_POINT 5 + +/* Matrix concatenation types */ +#define PRE 0 +#define POST 1 +#define REPLACE 0 +#define PUSH 1 + +/* Viewing matrix change types */ +#define REPLACE_VW 0 +#define PRE_CONCAT_VW 1 +#define POST_CONCAT_VW 2 + +/* character switching modes */ +#define ISO_7BIT 0 +#define ISO_8BIT 1 +#define HP_8BIT 2 + +/* text precision types */ +#define STRING_TEXT 0 +#define CHARACTER_TEXT 1 +#define STROKE_TEXT 2 + +/* text transformation types */ +#define VDC_TEXT 0 +#define WORLD_COORDINATE_TEXT 1 +#define TOS_TEXT 2 +#define ANNOTATION_TEXT 3 + +/* text alignment enumerated types */ +#define TA_LEFT 0 +#define SB_TA_CENTER 1 +#define TA_RIGHT 2 +#define TA_CONTINUOUS_HORIZONTAL 3 +#define TA_NORMAL_HORIZONTAL 4 + +#define TA_TOP 0 +#define TA_CAP 1 +#define TA_HALF 2 +#define TA_BASE 3 +#define SB_TA_BOTTOM 4 +#define TA_CONTINUOUS_VERTICAL 5 +#define TA_NORMAL_VERTICAL 6 + +/* character path and line path enumerated types */ +#define PATH_RIGHT 0 +#define PATH_LEFT 1 +#define PATH_UP 2 +#define PATH_DOWN 3 + +/* input device class enumerated types */ +#define ALL 0 +#define LOCATOR 1 +#define CHOICE 4 + +/* event queue states */ +#define EMPTY_NO_OVERFLOW 0 +#define EMPTY_OVERFLOW 1 +#define NOT_EMPTY_NO_OVERFLOW 2 +#define NOT_EMPTY_OVERFLOW 3 + +/* event message link enumerated types */ +#define SIMULTANEOUS_EVENT_FOLLOWS 0 +#define SINGLE_EVENT 1 + +/* gerr printing flags */ +#define NO_ERROR_PRINTING 0 +#define PRINT_ERRORS 1 +#define PRINT_WARNINGS 2 + +/* arc close_types */ +#define NO_CHORD 0 +#define PIE 1 +#define CHORD 2 + +/* spline orders and rationalities */ +#define NONRATIONAL 0 +//#define RATIONAL 1 +#define LINEAR 2 +#define QUADRATIC 3 +#define CUBIC 4 +#define QUARTIC 5 +#define QUINTIC 6 +#define DC_VALUES 0 +#define VDC_VALUES 2 +#define STEP_SIZE 3 + +/* transform_point modes */ +#define MC_TO_WC 0 +#define MC_TO_WORLD 0 +#define MC_TO_VDC 1 +#define WC_TO_VDC 2 +#define WORLD_TO_VDC 2 +#define WC_TO_MC 3 +#define WORLD_TO_MC 3 +#define VDC_TO_MC 4 +#define VDC_TO_WC 5 +#define VDC_TO_WORLD 5 +#define INTVDC_TO_DC 6 +#define DC_TO_INTVDC 7 + +/* view_camera projection types */ +#define CAM_PERSPECTIVE 0.0 +#define CAM_PARALLEL 1.0 + +/* plane printing modes */ +#define ALL_PLANES -1 +#define PIXEL_MAJOR -1 +#define PLANE_MAJOR -2 + +/* hatch types */ +#define PARALLEL_HATCH 0 +#define CROSSHATCH 1 + +/* highlight attribute types */ +#define HL_COLOR 1 +#define HL_STYLE 2 + +/* Hardware cursor control types */ +#define REQUEST_HW_CURSOR 1 +#define REQUEST_SW_CURSOR 2 +#define FORCE_HW_CURSOR 3 +#define REQUEST_HW_ECHO 1 +#define REQUEST_SW_ECHO 2 +#define FORCE_HW_ECHO 3 + +/* cgm encoding types */ +#define CGM_BINARY 1 +#define CGM_CHARACTER 2 +#define CGM_CLEAR_TEXT 3 + +/* GLOBAL gescapes */ +#define SWITCH_SEMAPHORE 0 +#define READ_COLOR_MAP 1 +#define BLINK_PLANES 2 +#define BLINK_INDEX 3 + +/* GLOBAL raster gescapes */ +#define R_GET_FRAME_BUFFER 20 +#define R_LOCK_DEVICE 21 +#define R_UNLOCK_DEVICE 22 +#define R_GET_WINDOW_INFO 23 +#define R_FULL_FRAME_BUFFER 24 +#define R_ALLOC_OFFSCREEN 25 +#define R_FREE_OFFSCREEN 26 +#define R_BIT_MODE 27 +#define R_BIT_MASK 28 +#define R_DEF_FILL_PAT 29 +#define R_OVERLAY_ECHO 30 +#define R_OV_ECHO_COLORS 31 +#define R_DEF_ECHO_TRANS 32 +#define R_TRANSPARENCY_INDEX 33 +#define R_LINE_TYPE 34 +#define R_ECHO_FG_BG_COLORS 35 +#define R_DMA_MODE 36 +#define R_ECHO_MASK 37 +#define R_ECHO_CONTROL 38 +#define R_OFFSCREEN_ALLOC 1106 +#define R_OFFSCREEN_FREE 1107 + +/* HPGL gescapes */ +#define HPGL_WRITE_BUFFER 100 +#define HPGL_SET_PEN_NUM 101 +#define HPGL_SET_PEN_SPEED 102 +#define HPGL_SET_PEN_WIDTH 103 +#define HPGL_READ_BUFFER 104 + +/* HPGL2 gescapes */ +#define HPGL2_SET_MEDIA_TYPE 105 +#define HPGL2_LOGICAL_PEN_WIDTH 106 +#define HPGL2_CUTTER_CONTROL 107 +#define HPGL2_REPLOT 108 +#define HPGL2_FONT_TYPEFACE 109 +#define HPGL2_ADAPTIVE_LINES 110 +#define HPGL2_SET_QUALITY 111 +#define HPGL2_SET_CMAP_SIZE 112 +#define HPGL2_FONT_WEIGHT 113 +#define HPGL2_FONT_POSTURE 114 + +/* HP26XX gescapes */ +#define HP26_PRINT_ESC 200 +#define HPTERM_PRINT_ESC 200 +#define HPTERM_640x400 201 + +/* 98700 gescapes */ +#define GB_NONE 300 + +/* 98710 gescapes */ +#define GA_NONE 400 + +/* 300l gescapes */ +#define TC_HALF_PIXEL 500 + +/* HIL and keyboard gescapes */ +#define ENABLE_AUTO_PROMPT 600 +#define DISABLE_AUTO_PROMPT 601 +#define PROMPT_ON 602 +#define PROMPT_OFF 603 +#define TRIGGER_ON_RELEASE 604 +#define IGNORE_RELEASE 605 +#define REPORT_PROXIMITY 606 +#define IGNORE_PROXIMITY 607 +#define ENABLE_ACKNOWLEDGE 608 +#define DISABLE_ACKNOWLEDGE 609 +#define SET_ACCELERATION 610 /* hil acceleration multiplier */ + +/* GKSM gescapes */ +#define GKSM_WRITE_ITEM 700 +#define GKSM_GET_ITEM_TYPE 701 +#define GKSM_READ_ITEM 702 +#define GKSM_SKIP_ITEM 703 +#define GKSM_INQ_COLOR_NDCES 704 +#define GKSM_INQ_PAT_REP 705 + +/* 98721 gescapes */ +#define TRANSPARENCY 800 +#define ZBUFFER_ALLOC 801 +#define LS_OVERFLOW_CONTROL 802 +#define PATTERN_FILL 803 +#define ZWRITE_ENABLE 804 +#define ZSTATE_SAVE 805 +#define ZSTATE_RESTORE 806 + +/* SMDpixel and SMDpixel3 gescapes */ +#define SMD_SUPPLY_MEM_BUFF 900 +#define SMD_GET_MEM_REQUIRED 901 +#define SMD_DEFINE_XY 902 +#define SMD_DEFINE_DEPTH 903 +#define SMD_ALLOCATE_MEMORY 904 + +/* Xn gescapes */ +#define XN_INPUT_RAW 1000 +/* raw mode (TRUE) returns LK201 keycodes + cooked mode (FALSE) returns ASCII */ +#define XN_KEY_RELEASE 1001 /* turn on/off key release events */ +#define XN_BUTTON_RELEASE 1002 /* turn on/off button release events */ + +/* 98549/49/50/56 gescapes */ +#define GR2D_MASK_ENABLE 1100 +#define GR2D_MASK_RULE 1101 +#define GR2D_DEF_MASK 1102 +#define GR2D_FILL_PATTERN 1103 +#define GR2D_OVERLAY_TRANSPARENT 1104 +#define GR2D_REPLICATE 1105 +#define GR2D_ALLOC_OFFSCREEN 1106 +#define GR2D_FREE_OFFSCREEN 1107 +#define GR2D_PLANE_MASK 1108 +#define GR2D_INQ_CLIST_ADDR 1109 +#define GR2D_LOAD_CLIST 1110 +#define GR2D_CONVEX_POLYGONS 1112 + +/* hp98730/31 driver gescapes */ +#define PAN_AND_ZOOM 1200 +#define OVERLAY_BLEND 1201 +#define IMAGE_BLEND 1202 +#define SET_BANK_CMAP 1203 +#define GAMMA_CORRECTION 1204 +#define INQ_GAMMA_CORRECTION 1205 +#define FULL_COLOR_INDEX 1206 +#define POLYGON_TRANSPARENCY 1207 +#define CLIP_OVERFLOW 1208 + +/* hp98704/05 gescapes */ +#define SET_REPLACEMENT_RULE 1250 + +/* hpcgm driver gescapes */ +#define CGMESC_ENCODING 1300 +#define CGMESC_ESCAPE_ELT 1301 +#define CGMESC_MET_NAME 1302 +#define CGMESC_PIC_NAME 1303 +#define CGMESC_FONT_IX 1304 +#define CGMESC_MESSAGE 1305 +#define CGMESC_APPL_DATA 1306 +#define CGMESC_VDC_PREC 1307 +#define CGMESC_TOP_MODE 1308 + +typedef union +{ + int i[64]; + float f[64]; + char c[255]; +} gescape_arg; + +typedef struct +{ + float refx, refy, refz; + float camx, camy, camz; + float upx, upy, upz; + float field_of_view; + float front, back; + float projection; +} camera_arg; +#endif /* _STARBASE_INCLUDED */ diff --git a/viewer/sbgtypes.h b/viewer/sbgtypes.h new file mode 100644 index 0000000000..4c2f917749 --- /dev/null +++ b/viewer/sbgtypes.h @@ -0,0 +1,248 @@ +/********************************************************************** + * File: sbgtypes.h (Formerly sbtypes.h) + * Description: Structures for the Starbase/X daemon interface. + * Author: Ray Smith + * Created: Wed May 16 09:26:35 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef SBGTYPES_H +#define SBGTYPES_H + +//This file contains all the symbols and types that are not of concern +//to the user, but are common to both the client and the sbdaemon side. + +#include "sbgconst.h" +#ifdef __UNIX__ +#include +#endif + +#define CHECK_TIME 1000 /*1 millisecond */ +#define AWAIT_TIME 999999 /*1 second */ +#define INFD 0 /*index to fds */ +#define OUTFD 1 /*for read/write */ +#define EVENTSIZE 16 //event buffer +#define PRIMITIVES (*(INT32*)((char*)shminfo.shmstart+shminfo.shmsize)) +#define EVENT_TAIL (*(INT32*)((char*)shminfo.shmstart+shminfo.shmsize+sizeof(INT32))) +#define EVENT_HEAD (*(INT32*)((char*)shminfo.shmstart+shminfo.shmsize+2*sizeof(INT32))) +#define EVENT_INDEX(index) (((SBD_GRAPHICS_EVENT*)((char*)shminfo.shmstart+shminfo.shmsize+3*sizeof(INT32)))[index]) + +typedef enum { + LINECOLOUR, /*line_color_index */ + PERIMETERCOLOUR, /*perimeter_color_index */ + FILLCOLOUR, /*fill_color_index */ + MARKERCOLOUR, /*marker_color_index */ + TEXTCOLOUR, /*text_color_index */ + TEXTFONT, /*text_font_index */ + CHARHEIGHT, /*character_height */ + LINETYPE, /*line_type */ + MARKERTYPE, /*marker_type */ + MARKERSIZE, /*marker_size */ + MARKERMODE, /*mode in markersize */ + INTERIORSTYLE, /*interior_style */ + EDGESTYLE, /*edge in interior_style */ + + MOVE2D, /*move2d */ + DRAW2D, /*draw2d */ + RECTANGLE, /*rectangle */ + TEXT_ALIGNMENT, //alginment + POLYLINE2D, /*polyline2d */ + POLYGON2D, /*polygon2d */ + POLYMARKER2D, /*polymarker2d */ + TEXT2D, /*text2d */ + APPENDTEXT, /*append_text */ + ELLIPSE, /*ellipse */ + ARC, /*arc */ + + SHOWIMAGE, /*display image */ + SHOWLINE, /*send image line */ + + CREATE, /*create_window */ + DESTROY, /*destroy_window */ + CLEAR, /*clear_view_surface */ + VDCEXTENT, /*vdc_extent */ + MAKECURRENT, /*make_picture_current */ + SETSIGNALS, //add callback + SETECHO, //change cursor + SYNCWIN, //sychronize + RE_COMP_COLMAP //Re-compute colourmap +} CALL_TYPE; /*type of call */ + +typedef union +{ + INT16 fd; /*input fd */ + void *next; /*turns to link */ +} HEADUNION; /*header of structure */ + +typedef union +{ + float f; /*parameter union */ + INT32 i; /*to reduce structures */ +} PARAMUNION; + +typedef struct +{ + PARAMUNION p; /*single parameter */ +} ONEPARAM; + +typedef struct +{ + PARAMUNION p[2]; /*two params */ +} TWOPARAMS; + +typedef struct +{ + PARAMUNION p[4]; /*4 params */ +} FOURPARAMS; + +typedef struct +{ + PARAMUNION p[8]; /*8 params */ +} EIGHTPARAMS; + +typedef struct +{ + HEADUNION header; /*fd/next op */ + CALL_TYPE type; /*call type */ + ONEPARAM param; /*single parameter */ +} ONEOP; + +typedef struct +{ + HEADUNION header; /*fd/next op */ + CALL_TYPE type; /*call type */ + TWOPARAMS param; /*single parameter */ +} TWOOP; + +typedef struct +{ + HEADUNION header; /*fd/next op */ + CALL_TYPE type; /*call type */ + FOURPARAMS param; /*single parameter */ +} FOUROP; + +typedef struct +{ + HEADUNION header; /*fd/next op */ + CALL_TYPE type; /*call type */ + EIGHTPARAMS param; /*single parameter */ +} EIGHTOP; + +typedef struct +{ + float *clist; /*coord list */ + INT32 numpts; /*number of coords */ + INT32 flags; /*move/draws on/off */ +} POLYPARAM; /*polygon params */ + +typedef struct +{ + HEADUNION header; /*fd/next */ + CALL_TYPE type; /*operator type */ + POLYPARAM param; /*parameters */ + float polyxmin, polyxmax; /*bounding box */ + float polyymin, polyymax; /*of polyline */ + float clist[1]; /*place holder */ +} POLYOP; /*poly line/marker */ + +typedef struct +{ + float x, y; /*coords of text */ + char *string; /*string to draw */ + INT32 xform; /*coord transform */ + INT32 more; /*any more text */ +} TEXTPARAM; /*text parameters */ + +typedef struct +{ + char *string; /*string to draw */ + INT32 xform; /*coord transform */ + INT32 more; /*any more text */ +} APPENDPARAM; /*text parameters */ + +typedef struct +{ + HEADUNION header; /*fd/next */ + CALL_TYPE type; /*operator type */ + TEXTPARAM param; /*parameters */ + char chars[4]; /*place holder */ +} TEXTOP; /*poly line/marker */ + +typedef struct +{ + HEADUNION header; /*fd/next */ + CALL_TYPE type; /*operator type */ + APPENDPARAM param; /*parameters */ + char chars[4]; /*place holder */ +} APPENDOP; /*poly line/marker */ + +typedef struct +{ + HEADUNION header; /*fd/next */ + CALL_TYPE type; /*operator type */ + INT32 size; /*size of structure */ + UINT8 line[2]; /*image line */ +} IMAGEOP; /*image passing */ + +typedef struct +{ + HEADUNION header; /*fd/next */ + CALL_TYPE type; /*operator type */ + INT16 xpos, ypos; /*initial position */ + INT16 xsize, ysize; /*initial size */ + float xmin, xmax; /*scrolling limits */ + float ymin, ymax; + BOOL8 downon; /*events required */ + BOOL8 moveon; + BOOL8 upon; + BOOL8 keyon; + INT32 window_type; /*false for SMD */ + char name[MAXWINDOWNAME]; /*name of window */ +} CREATEOP; + +typedef struct +{ + #ifdef __UNIX__ + int fds[2]; /*I/O files */ + int shmid; /*shared memory id */ + #else + #ifdef __PCDEMON__ + int fds[2]; /*I/O files */ + #else + HANDLE fds[2]; /*I/O files */ + #endif + HANDLE shmid; //handle to it + #endif + void *shmstart; /*addr of shm seg */ + INT32 usedsize; /*amount used */ + INT32 shmsize; /*size of shm seg */ + #ifdef __UNIX + pid_t pid; /*child process id */ + #endif +} SHMINFO; /*shared memory info */ + +typedef struct sbdgraphicsevent +{ + struct sbdgraphicsevent *next; /*next event */ + INT16 fd; //unix only + INT8 type; /*event type */ + char key; /*keypress */ + float x, y; /*position of event */ +} SBD_GRAPHICS_EVENT; /*event type */ + +//typedef void (*SBFUNC)(WINFD*,...); /*starbase function*/ +typedef void (*DELFUNC) (void *, INT32); + /*deletion function */ +typedef INT16 SBDWINDOW; +#endif diff --git a/viewer/showim.cpp b/viewer/showim.cpp new file mode 100644 index 0000000000..ca0b0c23ce --- /dev/null +++ b/viewer/showim.cpp @@ -0,0 +1,84 @@ +/********************************************************************** + * File: showimg.c (Formerly showim.c) + * Description: Interface to sbdaemon for displaying images. + * Author: Ray Smith + * Created: Mon Jun 11 16:20:34 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "grphshm.h" +#include "showim.h" + +void (*show_func) (IMAGE *, INT32, INT32, INT32, INT32, WINDOW, INT32, +INT32) = def_show_sub_image; + +/********************************************************************** + * show_sub_image + * + * Send the given image to the daemon and insert it in the display list. + **********************************************************************/ + +DLLSYM void def_show_sub_image( //show this image + IMAGE *source, //image to show + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to show + INT32 yext, + WINDOW win, //window to draw in + INT32 xpos, //position to show at + INT32 ypos //y position + ) { + EIGHTOP *newop; //message structure + INT32 y; //y coord + INT32 linelength; //bytes per line + IMAGE dummyimage; //used for copying to + IMAGEOP *sendline; //transmitted line + INT32 structsize; //size of structure + INT32 destbpp; //destination bits per pixel + + destbpp = source->get_bpp (); //send all images unchanged + + newop = (EIGHTOP *) getshm (sizeof (EIGHTOP)); + if (newop != NULL) { + //send the fd + newop->header.fd = win->get_fd (); + newop->type = SHOWIMAGE; //send the operator + newop->param.p[0].i = xext; //send parameters + newop->param.p[1].i = yext; + newop->param.p[2].i = destbpp; + newop->param.p[3].i = xpos; + newop->param.p[4].i = ypos; + + //bytes required + linelength = COMPUTE_IMAGE_XDIM (xext, destbpp); + linelength++; //round up to next + linelength &= ~1; //multiple of 2 + //size of structure + structsize = (INT32) (sizeof (IMAGEOP) + linelength - 2); + for (y = yext - 1; y >= 0; --y) { + //get space + sendline = (IMAGEOP *) getshm (structsize); + if (sendline != NULL) { + sendline->header.fd = win->get_fd (); + sendline->type = SHOWLINE; + sendline->size = structsize; + dummyimage.capture (sendline->line, xext, 1, (INT8) destbpp); + //ready for copy + //copy to shm + copy_sub_image (source, xstart, ystart + y, xext, 1, &dummyimage, 0, 0, FALSE); + } + } + } +} diff --git a/viewer/showim.h b/viewer/showim.h new file mode 100644 index 0000000000..ea906f19a6 --- /dev/null +++ b/viewer/showim.h @@ -0,0 +1,41 @@ +/********************************************************************** + * File: showimg.c (Formerly showim.c) + * Description: Interface to sbdaemon for displaying images. + * Author: Ray Smith + * Created: Mon Jun 11 16:20:34 BST 1990 + * + * (C) Copyright 1990, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef SHOWIM_H +#define SHOWIM_H + +#include "grphics.h" +#include "img.h" + +#define show_sub_image(im,xstart,ystart,xext,yext,win,xpos,ypos) (*show_func)(im,xstart,ystart,xext,yext,win,xpos,ypos) + +extern void (*show_func) (IMAGE *, INT32, INT32, INT32, INT32, WINDOW, INT32, +INT32); + +DLLSYM void def_show_sub_image( //show this image + IMAGE *source, //image to show + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to show + INT32 yext, + WINDOW win, //window to draw in + INT32 xpos, //position to show at + INT32 ypos //y position + ); +#endif diff --git a/wordrec/Makefile.am b/wordrec/Makefile.am new file mode 100644 index 0000000000..22c049bd92 --- /dev/null +++ b/wordrec/Makefile.am @@ -0,0 +1,23 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccstruct -I$(top_srcdir)/ccutil \ + -I$(top_srcdir)/cutil -I$(top_srcdir)/classify \ + -I$(top_srcdir)/image -I$(top_srcdir)/dict \ + -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + associate.h badwords.h bestfirst.h charsample.h chop.h \ + chopper.h closed.h djmenus.h drawfx.h findseam.h gradechop.h \ + heuristic.h makechop.h matchtab.h matrix.h measure.h metrics.h \ + mfvars.h msmenus.h olutil.h outlines.h pieces.h plotedges.h \ + plotseg.h render.h seam.h split.h tally.h tessinit.h tface.h \ + wordclass.h + +noinst_LIBRARIES = libtesseract_wordrec.a +libtesseract_wordrec_a_SOURCES = \ + associate.cpp badwords.cpp bestfirst.cpp chop.cpp chopper.cpp \ + closed.cpp djmenus.cpp drawfx.cpp findseam.cpp gradechop.cpp \ + heuristic.cpp makechop.cpp matchtab.cpp matrix.cpp metrics.cpp \ + mfvars.cpp msmenus.cpp olutil.cpp outlines.cpp pieces.cpp \ + plotedges.cpp plotseg.cpp render.cpp seam.cpp split.cpp \ + tally.cpp tessinit.cpp tface.cpp wordclass.cpp diff --git a/wordrec/Makefile.in b/wordrec/Makefile.in new file mode 100644 index 0000000000..1147f305e4 --- /dev/null +++ b/wordrec/Makefile.in @@ -0,0 +1,578 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = wordrec +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_wordrec_a_AR = $(AR) $(ARFLAGS) +libtesseract_wordrec_a_LIBADD = +am_libtesseract_wordrec_a_OBJECTS = associate.$(OBJEXT) \ + badwords.$(OBJEXT) bestfirst.$(OBJEXT) chop.$(OBJEXT) \ + chopper.$(OBJEXT) closed.$(OBJEXT) djmenus.$(OBJEXT) \ + drawfx.$(OBJEXT) findseam.$(OBJEXT) gradechop.$(OBJEXT) \ + heuristic.$(OBJEXT) makechop.$(OBJEXT) matchtab.$(OBJEXT) \ + matrix.$(OBJEXT) metrics.$(OBJEXT) mfvars.$(OBJEXT) \ + msmenus.$(OBJEXT) olutil.$(OBJEXT) outlines.$(OBJEXT) \ + pieces.$(OBJEXT) plotedges.$(OBJEXT) plotseg.$(OBJEXT) \ + render.$(OBJEXT) seam.$(OBJEXT) split.$(OBJEXT) \ + tally.$(OBJEXT) tessinit.$(OBJEXT) tface.$(OBJEXT) \ + wordclass.$(OBJEXT) +libtesseract_wordrec_a_OBJECTS = $(am_libtesseract_wordrec_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_wordrec_a_SOURCES) +DIST_SOURCES = $(libtesseract_wordrec_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccstruct -I$(top_srcdir)/ccutil \ + -I$(top_srcdir)/cutil -I$(top_srcdir)/classify \ + -I$(top_srcdir)/image -I$(top_srcdir)/dict \ + -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + associate.h badwords.h bestfirst.h charsample.h chop.h \ + chopper.h closed.h djmenus.h drawfx.h findseam.h gradechop.h \ + heuristic.h makechop.h matchtab.h matrix.h measure.h metrics.h \ + mfvars.h msmenus.h olutil.h outlines.h pieces.h plotedges.h \ + plotseg.h render.h seam.h split.h tally.h tessinit.h tface.h \ + wordclass.h + +noinst_LIBRARIES = libtesseract_wordrec.a +libtesseract_wordrec_a_SOURCES = \ + associate.cpp badwords.cpp bestfirst.cpp chop.cpp chopper.cpp \ + closed.cpp djmenus.cpp drawfx.cpp findseam.cpp gradechop.cpp \ + heuristic.cpp makechop.cpp matchtab.cpp matrix.cpp metrics.cpp \ + mfvars.cpp msmenus.cpp olutil.cpp outlines.cpp pieces.cpp \ + plotedges.cpp plotseg.cpp render.cpp seam.cpp split.cpp \ + tally.cpp tessinit.cpp tface.cpp wordclass.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu wordrec/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu wordrec/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_wordrec.a: $(libtesseract_wordrec_a_OBJECTS) $(libtesseract_wordrec_a_DEPENDENCIES) + -rm -f libtesseract_wordrec.a + $(libtesseract_wordrec_a_AR) libtesseract_wordrec.a $(libtesseract_wordrec_a_OBJECTS) $(libtesseract_wordrec_a_LIBADD) + $(RANLIB) libtesseract_wordrec.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/associate.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/badwords.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestfirst.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chop.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chopper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/closed.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/djmenus.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drawfx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/findseam.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gradechop.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heuristic.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/makechop.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/matchtab.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/matrix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metrics.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfvars.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/msmenus.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/olutil.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/outlines.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pieces.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plotedges.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plotseg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/render.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/seam.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/split.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tally.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessinit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tface.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wordclass.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/wordrec/associate.cpp b/wordrec/associate.cpp new file mode 100644 index 0000000000..31b6cb3835 --- /dev/null +++ b/wordrec/associate.cpp @@ -0,0 +1,62 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: associate.c (Formerly associate.c) + * Description: Associate the outlines and classify them + * Author: Mark Seaman, OCR Technology + * Created: Tue Jan 30 14:03:25 1990 + * Modified: Mon Jul 22 10:48:01 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + */ + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include +#ifdef __UNIX__ +#include +#endif + +#include "associate.h" +#include "debug.h" +#include "callcpp.h" + +extern TBLOB *newblob(); + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ + +make_toggle_var (enable_assoc, 1, make_enable_assoc, +9, 4, toggle_assoc, "Associator Enable"); + +EVALUATION_ARRAY last_segmentation; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ + +/********************************************************************** + * init_associate_vars + * + * Create and initialize references to debug variables that control + * operations in this file. + **********************************************************************/ +void init_associate_vars() { + make_enable_assoc(); +} diff --git a/wordrec/associate.h b/wordrec/associate.h new file mode 100644 index 0000000000..2a5fba14b4 --- /dev/null +++ b/wordrec/associate.h @@ -0,0 +1,93 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: associate.h (Formerly associate.h) + * Description: Associate the outlines and classify them + * Author: Mark Seaman, OCR Technology + * Created: Mon Feb 5 11:42:51 1990 + * Modified: Tue May 21 15:34:56 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + */ + +#ifndef ASSOCIATE_H +#define ASSOCIATE_H + +/* +---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------- +*/ + +#include "matrix.h" +#include "states.h" +#include "blobs.h" +#include "split.h" +#include "seam.h" + +/* +---------------------------------------------------------------------- + T y p e s +---------------------------------------------------------------------- +*/ + +typedef LIST BLOBS; //? /* List of (BLOB*) */ + +typedef LIST OUTLINES; /* List of (TESSLINE*) */ + +typedef LIST EDGEPTS; /* List of (EDGEPT*) */ + +typedef INT16 BLOB_WEIGHTS[MAX_NUM_CHUNKS]; + +typedef struct +{ /* Each char evaluated */ + float match; + float certainty; + char character; + int width; + int gap; +} EVALUATION_RECORD; + +typedef struct +{ /* Classification info */ + MATRIX ratings; /* for chunks */ + TBLOB *chunks; + SEAMS splits; + TEXTROW *row; + int fx; + int x_height; + WIDTH_RECORD *chunk_widths; + WIDTH_RECORD *char_widths; + INT16 *weights; +} CHUNKS_RECORD; + + /* Each segmentation */ +typedef EVALUATION_RECORD EVALUATION_ARRAY[MAX_NUM_CHUNKS]; + +/*---------------------------------------------------------------------- + V a r i a b l e s + ----------------------------------------------------------------------*//* Debug vars */ +extern int enable_assoc; +extern EVALUATION_ARRAY last_segmentation; +extern WIDTH_RECORD *char_widths; +extern int enable_assoc; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void init_associate_vars(); +#endif diff --git a/wordrec/badwords.cpp b/wordrec/badwords.cpp new file mode 100644 index 0000000000..99cf93ba47 --- /dev/null +++ b/wordrec/badwords.cpp @@ -0,0 +1,106 @@ +/****************************************************************************** + ** Filename: badwords.c + ** Purpose: Routines to keep the bad words in sorted order. + ** Author: Dan Johnson + ** History: Thu Apr 25 08:40:19 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "general.h" +#include "oldheap.h" +#include "callcpp.h" + +#include +#include +#ifdef __UNIX__ +#include +#endif + +#define MAX_NUM_BAD_WERDS 1000 + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +static HEAP *BadWords = NULL; +BOOL_VAR (tessedit_save_stats, FALSE, "Save final recognition statistics"); + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void PrintBadWords(FILE *File) { +/* + ** Parameters: + ** File open text file to print bad words to + ** Globals: + ** BadWords heap that bad words are stored in + ** Operation: This routine prints the bad words stored in BadWords + ** to file ordered by certainty (worst certainty first). + ** Return: none + ** Exceptions: none + ** History: Thu Apr 25 08:57:08 1991, DSJ, Created. + */ + HEAPENTRY NextWord; + + if (BadWords == NULL) + return; + + fprintf (File, "\n"); + fprintf (File, "Bad Word Certainty\n"); + fprintf (File, "---------------- ---------\n"); + while (GetTopOfHeap (BadWords, &NextWord) != EMPTY) { + fprintf (File, "%16s %6.2f\n", (const char *) NextWord.Data, + NextWord.Key); + c_free_string ((char *) NextWord.Data); + } + fprintf (File, "\n"); + +} /* PrintBadWords */ + + +/*---------------------------------------------------------------------------*/ +void SaveBadWord(const char *Word, FLOAT32 Certainty) { +/* + ** Parameters: + ** Word bad word to be saved + ** Certainty certainty of word + ** Globals: + ** BadWords heap to keep bad words in + ** Operation: This routine saves all words flagged as bad in a heap + ** with the worst word on the top of the heap. The contents + ** of this heap can be printed to a file by calling + ** PrintBadWords (File). + ** Return: none + ** Exceptions: none + ** History: Thu Apr 25 08:41:00 1991, DSJ, Created. + */ + HEAPENTRY NewWord; + + assert (Word != NULL); + + if (BadWords == NULL) { + BadWords = MakeHeap (MAX_NUM_BAD_WERDS); + InitHeap(BadWords); + } else if (HeapFull(BadWords)) { + return; + } + + NewWord.Key = Certainty; + NewWord.Data = c_alloc_string (strlen (Word) + 1); + strcpy ((char *) NewWord.Data, Word); + HeapStore(BadWords, &NewWord); + +} /* SaveBadWord */ diff --git a/wordrec/badwords.h b/wordrec/badwords.h new file mode 100644 index 0000000000..93f60e451e --- /dev/null +++ b/wordrec/badwords.h @@ -0,0 +1,51 @@ +/****************************************************************************** + ** Filename: badwords.h + ** Purpose: Routines to keep the bad words in sorted order. + ** Author: Dan Johnson + ** History: Thu Apr 25 09:06:48 1991, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef __BADWERDS__ +#define __BADWERDS__ + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void PrintBadWords(FILE *File); + +void SaveBadWord(const char *Word, FLOAT32 Certainty); +extern BOOL_VAR_H (tessedit_save_stats, FALSE, "Save final recognition statistics"); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* badwords.c +void PrintBadWords + _ARGS((FILE *File)); + +void SaveBadWord + _ARGS((char *Word, + FLOAT32 Certainty)); + +#undef _ARGS +*/ +#endif diff --git a/wordrec/bestfirst.cpp b/wordrec/bestfirst.cpp new file mode 100644 index 0000000000..e77b79a7d5 --- /dev/null +++ b/wordrec/bestfirst.cpp @@ -0,0 +1,512 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: bestfirst.c (Formerly bestfirst.c) + * Description: Best first search functions + * Author: Mark Seaman, OCR Technology + * Created: Mon May 14 11:23:29 1990 + * Modified: Tue Jul 30 16:08:47 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ***************************************************************************/ + +/*---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------*/ +#include "bestfirst.h" +#include "heuristic.h" +#include "plotseg.h" +#include "tordvars.h" +#include "debug.h" +#include "pieces.h" +#include "stopper.h" +#include "metrics.h" +#include "states.h" +#include "bitvec.h" +#include "freelist.h" +#include "permute.h" +#include "structures.h" +#include "wordclass.h" + +void call_caller(); + +/*---------------------------------------------------------------------- + V a r i a b l e s +---------------------------------------------------------------------*/ +int num_joints; /* Number of chunks - 1 */ +int num_pushed = 0; +int num_popped = 0; + +make_int_var (num_seg_states, 30, make_seg_states, +9, 1, set_seg_states, "Segmentation states"); + +make_float_var (worst_state, 1, make_worst_state, +9, 9, set_worst_state, "Worst segmentation state"); +/**/ +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * init_bestfirst_vars + * + * Create and initialize references to debug variables that control + * operations in this file. + **********************************************************************/ +void init_bestfirst_vars() { + make_seg_states(); + make_worst_state(); +} + + +/********************************************************************** + * best_first_search + * + * Find the best segmentation by doing a best first search of the + * solution space. + **********************************************************************/ +void best_first_search(CHUNKS_RECORD *chunks_record, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + STATE *state, + DANGERR *fixpt, + STATE *best_state, + INT32 pass) { + SEARCH_RECORD *the_search; + INT16 keep_going; + STATE guided_state; + + num_joints = matrix_dimension (chunks_record->ratings) - 1; + the_search = new_search (chunks_record, num_joints, + best_choice, raw_choice, state); + +#ifndef GRAPHICS_DISABLED + save_best_state(chunks_record); +#endif + start_recording(); + + guided_state = *state; + do { + /* Look for answer */ + if (!hash_lookup (the_search->closed_states, the_search->this_state)) { + + if (blob_skip) { + free_state (the_search->this_state); + break; + } + + guided_state = *(the_search->this_state); + keep_going = + evaluate_state(chunks_record, the_search, fixpt, best_state, pass); + + hash_add (the_search->closed_states, the_search->this_state); + + if (!keep_going || + (the_search->num_states > num_seg_states) || (blob_skip)) { + free_state (the_search->this_state); + break; + } + + expand_node(chunks_record, the_search); + } + + free_state (the_search->this_state); + num_popped++; + the_search->this_state = pop_queue (the_search->open_states); + } + while (the_search->this_state); + + state->part1 = the_search->best_state->part1; + state->part2 = the_search->best_state->part2; + stop_recording(); + delete_search(the_search); +} + + +/********************************************************************** + * chunks_width + * + * Return the width of several of the chunks (if they were joined to- + * gether. + **********************************************************************/ +int chunks_width(WIDTH_RECORD *width_record, int start_chunk, int last_chunk) { + int result = 0; + int x; + + for (x = start_chunk * 2; x <= last_chunk * 2; x++) + result += width_record->widths[x]; + + return (result); +} + + +/********************************************************************** + * delete_search + * + * Terminate the current search and free all the memory involved. + **********************************************************************/ +void delete_search(SEARCH_RECORD *the_search) { + float closeness; + + closeness = (the_search->num_joints ? + (hamming_distance ((unsigned long *) the_search->first_state, + (unsigned long *) the_search->best_state, + 2) / (float) the_search->num_joints) : 0.0); + + record_search_status (the_search->num_states, + the_search->before_best, closeness); + + free_state (the_search->first_state); + free_state (the_search->best_state); + + free_hash_table (the_search->closed_states); + FreeHeapData (the_search->open_states, (void_dest) free_state); + + memfree(the_search); +} + + +/********************************************************************** + * evaluate_chunks + * + * A particular word level segmentation has been chosen. Evaluation + * this to find the word list that corresponds to it. + **********************************************************************/ +CHOICES_LIST evaluate_chunks(CHUNKS_RECORD *chunks_record, + SEARCH_STATE search_state, + STATE *this_state, + STATE *best_state, + INT32 pass) { + CHOICES_LIST char_choices; + CHOICES this_choice; + int i; + int x = 0; + int y; + + char_choices = new_choice_list (); + /* Iterate sub-paths */ + for (i = 1; i <= search_state[0] + 1; i++) { + if (i > search_state[0]) + y = count_blobs (chunks_record->chunks) - 1; + else + y = x + search_state[i]; + + if (blob_skip) { + array_free(char_choices); + return (NULL); + } /* Process one square */ + /* Classify if needed */ + this_choice = get_piece_rating (chunks_record->ratings, + chunks_record->chunks, + chunks_record->splits, + x, y, + chunks_record->fx, + this_state, best_state, pass, i - 1); + + if (this_choice == NIL) { + array_free(char_choices); + return (NULL); + } + /* Add permuted ratings */ + last_segmentation[i - 1].certainty = best_certainty (this_choice); + last_segmentation[i - 1].match = best_probability (this_choice); + + last_segmentation[i - 1].width = + chunks_width (chunks_record->chunk_widths, x, y); + last_segmentation[i - 1].gap = + chunks_gap (chunks_record->chunk_widths, y); + + char_choices = array_push (char_choices, this_choice); + x = y + 1; + } + return (char_choices); +} + + +/********************************************************************** + * evaluate_state + * + * Evaluate the segmentation that is represented by this state in the + * best first search. Add this state to the "states_seen" list. + **********************************************************************/ +INT16 evaluate_state(CHUNKS_RECORD *chunks_record, + SEARCH_RECORD *the_search, + DANGERR *fixpt, + STATE *best_state, + INT32 pass) { + CHOICES_LIST char_choices; + SEARCH_STATE chunk_groups; + float rating_limit = class_probability (the_search->best_choice); + INT16 keep_going = TRUE; + PIECES_STATE widths; + + the_search->num_states++; + chunk_groups = bin_to_chunks (the_search->this_state, + the_search->num_joints); + bin_to_pieces (the_search->this_state, the_search->num_joints, widths); + LogNewSegmentation(widths); + + rating_limit = class_probability (the_search->best_choice); + + char_choices = + evaluate_chunks (chunks_record, chunk_groups, the_search->this_state, + best_state, pass); + if (char_choices != NULL) { + permute_characters (char_choices, + rating_limit, + the_search->best_choice, the_search->raw_choice); + if (AcceptableChoice (char_choices, the_search->best_choice, + the_search->raw_choice, fixpt)) + keep_going = FALSE; + array_free(char_choices); + } + +#ifndef GRAPHICS_DISABLED + if (display_segmentations) { + display_segmentation (chunks_record->chunks, chunk_groups); + if (display_segmentations > 1) + window_wait(segm_window); + } +#endif + + if (rating_limit != class_probability (the_search->best_choice)) { + the_search->before_best = the_search->num_states; + the_search->best_state->part1 = the_search->this_state->part1; + the_search->best_state->part2 = the_search->this_state->part2; + replace_char_widths(chunks_record, chunk_groups); + } + else if (char_choices != NULL) + fixpt->index = -1; + + memfree(chunk_groups); + + return (keep_going); +} + + +/********************************************************************** + * rebuild_current_state + * + * Evaluate the segmentation that is represented by this state in the + * best first search. Add this state to the "states_seen" list. + **********************************************************************/ +CHOICES_LIST rebuild_current_state(TBLOB *blobs, + SEAMS seam_list, + STATE *state, + CHOICES_LIST old_choices, + int fx) { + CHOICES_LIST char_choices; + SEARCH_STATE search_state; + int i; + int num_joints = array_count (seam_list); + int x = 0; + int blobindex; /*current blob */ + TBLOB *p_blob; + TBLOB *blob; + TBLOB *next_blob; + int y; + + search_state = bin_to_chunks (state, num_joints); + + char_choices = new_choice_list (); + /* Iterate sub-paths */ + for (i = 1; i <= search_state[0]; i++) { + y = x + search_state[i]; + x = y + 1; + char_choices = array_push (char_choices, NULL); + } + char_choices = array_push (char_choices, NULL); + + y = count_blobs (blobs) - 1; + for (i = search_state[0]; i >= 0; i--) { + if (x == y) { /*single fragment */ + array_value (char_choices, i) = array_value (old_choices, x); + /*grab the list */ + array_value (old_choices, x) = NULL; + } + else { + join_pieces(blobs, seam_list, x, y); + for (blob = blobs, blobindex = 0, p_blob = NULL; blobindex < x; + blobindex++) { + p_blob = blob; + blob = blob->next; + } + while (blobindex < y) { + next_blob = blob->next; + blob->next = next_blob->next; + oldblob(next_blob); /*junk dead blobs */ + blobindex++; + } + array_value (char_choices, i) = + (char *) classify_blob (p_blob, blob, blob->next, NULL, fx, + "rebuild", Orange, NULL, NULL, 0, 0); + } + + y = x - 1; + x = y - search_state[i]; + } + + memfree(search_state); + free_all_choices(old_choices, x); + return (char_choices); + +} + + +/********************************************************************** + * expand_node + * + * Create the states that are attached to this one. Check to see that + * each one has not already been visited. If not add it to the priority + * queue. + **********************************************************************/ +void expand_node(CHUNKS_RECORD *chunks_record, SEARCH_RECORD *the_search) { + STATE old_state; + int x; + int mask = 1 << (the_search->num_joints - 1 - 32); + + old_state.part1 = the_search->this_state->part1; + old_state.part2 = the_search->this_state->part2; + + for (x = the_search->num_joints; x > 32; x--) { + the_search->this_state->part1 = mask ^ old_state.part1; + if (!hash_lookup (the_search->closed_states, the_search->this_state)) + push_queue (the_search->open_states, + the_search->this_state, + prioritize_state (chunks_record, the_search, &old_state)); + mask >>= 1; + } + + if (the_search->num_joints > 32) { + mask = 1 << 31; + } + else { + mask = 1 << (the_search->num_joints - 1); + } + + while (x--) { + the_search->this_state->part2 = mask ^ old_state.part2; + if (!hash_lookup (the_search->closed_states, the_search->this_state)) + push_queue (the_search->open_states, + the_search->this_state, + prioritize_state (chunks_record, the_search, &old_state)); + mask >>= 1; + } +} + + +/********************************************************************** + * new_search + * + * Create and initialize a new search record. + **********************************************************************/ +SEARCH_RECORD *new_search(CHUNKS_RECORD *chunks_record, + int num_joints, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + STATE *state) { + SEARCH_RECORD *this_search; + + this_search = (SEARCH_RECORD *) memalloc (sizeof (SEARCH_RECORD)); + + this_search->open_states = MakeHeap (num_seg_states * 20); + this_search->closed_states = new_hash_table (); + + if (state) + this_search->this_state = new_state (state); + else + cprintf ("error: bad initial state in new_search\n"); + + this_search->first_state = new_state (this_search->this_state); + this_search->best_state = new_state (this_search->this_state); + + this_search->best_choice = best_choice; + this_search->raw_choice = raw_choice; + + this_search->num_joints = num_joints; + this_search->num_states = 0; + this_search->before_best = 0; + + return (this_search); +} + + +/********************************************************************** + * pop_queue + * + * Get this state from the priority queue. It should be the state that + * has the greatest urgency to be evaluated. + **********************************************************************/ +STATE *pop_queue(HEAP *queue) { + HEAPENTRY entry; + + if (GetTopOfHeap (queue, &entry) == OK) { +#ifndef GRAPHICS_DISABLED + if (display_segmentations) { + cprintf ("eval state: %8.3f ", entry.Key); + print_state ("", (STATE *) entry.Data, num_joints); + } +#endif + return ((STATE *) entry.Data); + } + else { + return (NULL); + } +} + + +/********************************************************************** + * push_queue + * + * Add this state into the priority queue. + **********************************************************************/ +void push_queue(HEAP *queue, STATE *state, FLOAT32 priority) { + HEAPENTRY entry; + + if (SizeOfHeap (queue) < MaxSizeOfHeap (queue) && priority < worst_state) { + entry.Data = (char *) new_state (state); + num_pushed++; + entry.Key = priority; + HeapStore(queue, &entry); + } +} + + +/********************************************************************** + * replace_char_widths + * + * Replace the value of the char_width field in the chunks_record with + * the updated width measurements from the last_segmentation. + **********************************************************************/ +void replace_char_widths(CHUNKS_RECORD *chunks_record, SEARCH_STATE state) { + WIDTH_RECORD *width_record; + int num_blobs; + int i; + + free_widths (chunks_record->char_widths); + + num_blobs = state[0] + 1; + width_record = (WIDTH_RECORD *) memalloc (sizeof (int) * num_blobs * 2); + width_record->num_chars = num_blobs; + + for (i = 0; i < num_blobs; i++) { + + width_record->widths[2 * i] = last_segmentation[i].width; + + if (i + 1 < num_blobs) + width_record->widths[2 * i + 1] = last_segmentation[i].gap; + } + chunks_record->char_widths = width_record; +} diff --git a/wordrec/bestfirst.h b/wordrec/bestfirst.h new file mode 100644 index 0000000000..ebdf731042 --- /dev/null +++ b/wordrec/bestfirst.h @@ -0,0 +1,200 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: bestfirst.h (Formerly bestfirst.h) + * Description: Best first search functions + * Author: Mark Seaman, OCR Technology + * Created: Mon May 14 11:23:29 1990 + * Modified: Mon Apr 29 14:21:57 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *******************************************************************************/ + +#ifndef BESTFIRST_H +#define BESTFIRST_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "oldheap.h" +#include "closed.h" +#include "choicearr.h" +#include "associate.h" +#include "choices.h" +#include "states.h" +#include "stopper.h" +#include "blobs.h" +#include "tessclas.h" +#include "seam.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef struct +{ + HEAP *open_states; + HASH_TABLE closed_states; + STATE *this_state; + STATE *first_state; + STATE *best_state; + int num_joints; + long num_states; + long before_best; + A_CHOICE *best_choice; + A_CHOICE *raw_choice; +} SEARCH_RECORD; + +/*---------------------------------------------------------------------- + V a r i a b l e s +---------------------------------------------------------------------*/ +extern int num_seg_states; +extern int num_popped; + +/*---------------------------------------------------------------------- + M a c r o s +---------------------------------------------------------------------*/ +/********************************************************************** + * chunks_gap + * + * Return the width of several of the chunks (if they were joined to- + * gether. + **********************************************************************/ +#define chunks_gap(chunk_widths,last_chunk) \ +((last_chunk < (chunk_widths)->num_chars - 1) ? \ + ((chunk_widths)->widths[last_chunk * 2 + 1]) : \ + (0)) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void init_bestfirst_vars(); + +void best_first_search(CHUNKS_RECORD *chunks_record, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + STATE *state, + DANGERR *fixpt, + STATE *best_state, + INT32 pass); + +int chunks_width(WIDTH_RECORD *width_record, int start_chunk, int last_chunk); + +void delete_search(SEARCH_RECORD *the_search); + +CHOICES_LIST evaluate_chunks(CHUNKS_RECORD *chunks_record, + SEARCH_STATE search_state, + STATE *this_state, + STATE *best_state, + INT32 pass); + +INT16 evaluate_state(CHUNKS_RECORD *chunks_record, + SEARCH_RECORD *the_search, + DANGERR *fixpt, + STATE *best_state, + INT32 pass); + +CHOICES_LIST rebuild_current_state(TBLOB *blobs, + SEAMS seam_list, + STATE *state, + CHOICES_LIST old_choices, + int fx); + +void expand_node(CHUNKS_RECORD *chunks_record, SEARCH_RECORD *the_search); + +SEARCH_RECORD *new_search(CHUNKS_RECORD *chunks_record, + int num_joints, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + STATE *state); + +STATE *pop_queue(HEAP *queue); + +void push_queue(HEAP *queue, STATE *state, FLOAT32 priority); + +void replace_char_widths(CHUNKS_RECORD *chunks_record, SEARCH_STATE state); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* bestfirst.c +void init_bestfirst_vars + _ARGS((void)); + +void best_first_search + _ARGS((CHUNKS_RECORD *chunks_record, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + STATE *state, + STATE* best_state, + INT32 pass)); + +CHOICES_LIST rebuild_current_state(); + +void write_segmentation + _ARGS((char *correct, + CHUNKS_RECORD *chunks_record, + SEARCH_RECORD *the_search)); + +int chunks_width + _ARGS((WIDTH_RECORD *width_record, + int start_chunk, + int last_chunk)); + +void delete_search + _ARGS((SEARCH_RECORD *the_search)); + +CHOICES_LIST evaluate_chunks + _ARGS((CHUNKS_RECORD *chunks_record, + SEARCH_STATE search_state, + STATE* this_state, + STATE* best_state, + INT32 pass)); + +INT16 evaluate_state + _ARGS((CHUNKS_RECORD *chunks_record, + SEARCH_RECORD *the_search, + STATE* best_state, + INT32 pass)); + +void expand_node + _ARGS((CHUNKS_RECORD *chunks_record, + SEARCH_RECORD *the_search)); + +SEARCH_RECORD *new_search + _ARGS((CHUNKS_RECORD *chunks_record, + int num_joints, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + STATE *state)); + +STATE *pop_queue + _ARGS((HEAP *queue)); + +void push_queue + _ARGS((HEAP *queue, + STATE *state, + FLOAT32 priority)); + +void replace_char_widths + _ARGS((CHUNKS_RECORD *chunks_record, + SEARCH_STATE state)); +#undef _ARGS +*/ +#endif diff --git a/wordrec/charsample.h b/wordrec/charsample.h new file mode 100644 index 0000000000..c9a011c1cd --- /dev/null +++ b/wordrec/charsample.h @@ -0,0 +1,208 @@ +/********************************************************************** + * File: charsample.h (Formerly charsample.h) + * Description: Class to contain character samples and match scores + * to be used for adaption + * Author: Chris Newton + * Created: Thu Oct 7 13:40:37 BST 1993 + * + * (C) Copyright 1993, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef CHARSAMPLE_H +#define CHARSAMPLE_H + +#include "elst.h" +#include "pageres.h" +#include "memry.h" +#include "notdll.h" +#include "notdll.h" + +#define BAD_SCORE MAX_INT32 +#define FIRST_CHAR '!' +#define LAST_CHAR '~' + +enum ClusterType +{ UNKNOWN, BLOB_CLUSTER, IMAGE_CLUSTER }; + +class CHAR_SAMPLE; //forward decl + +ELISTIZEH (CHAR_SAMPLE) +class CHAR_SAMPLES; //forward decl + +ELISTIZEH (CHAR_SAMPLES) +class CHAR_PROTO; //forward decl + +class CHAR_SAMPLE:public ELIST_LINK +{ + public: + CHAR_SAMPLE(); // empty constructor + + CHAR_SAMPLE( // simple constructor + PBLOB *blob, + DENORM *denorm, + char c); + + CHAR_SAMPLE( // simple constructor + IMAGE *image, + char c); + + ~CHAR_SAMPLE () { + // We own the image, so it has to be deleted. + if (sample_image != NULL) + delete sample_image; + } + + float match_sample(CHAR_SAMPLE *test_sample, BOOL8 updating); + + INT32 n_matches() { + return n_samples_matched; + } + + IMAGE *image() { + return sample_image; + } + + PBLOB *blob() { + return sample_blob; + } + + DENORM *denorm() { + return sample_denorm; + } + + double mean_score(); + + double variance(); + + char character() { + return ch; + } + + void print(FILE *f); + + void reset_match_statistics(); + + NEWDELETE2 (CHAR_SAMPLE) private: + IMAGE * sample_image; + PBLOB *sample_blob; + DENORM *sample_denorm; + INT32 n_samples_matched; + double total_match_scores; + double sumsq_match_scores; + char ch; +}; + +class CHAR_SAMPLES:public ELIST_LINK +{ + public: + CHAR_SAMPLES(); //empty constructor + + CHAR_SAMPLES(CHAR_SAMPLE *sample); + + ~CHAR_SAMPLES () { //destructor + } + + INT32 n_samples() { + return samples.length (); + } + + void add_sample(CHAR_SAMPLE *sample); + + void build_prototype(); + + void rebuild_prototype(INT32 new_xsize, INT32 new_ysize); + + void add_sample_to_prototype(CHAR_SAMPLE *sample); + + CHAR_PROTO *prototype() { + return proto; + } + + void find_best_sample(); + + float match_score(CHAR_SAMPLE *sample); + + float nn_match_score(CHAR_SAMPLE *sample); + + char character() { + return ch; + } + + void assign_to_char(); + + void print(FILE *f); + + NEWDELETE2 (CHAR_SAMPLES) private: + ClusterType type; + char ch; + CHAR_PROTO *proto; + CHAR_SAMPLE *best_sample; + CHAR_SAMPLE_LIST samples; +}; + +class CHAR_PROTO +{ + public: + CHAR_PROTO(); // empty constructor + + CHAR_PROTO(INT32 x_size, + INT32 y_size, + INT32 n_samples, + float initial_value, + char c); + + CHAR_PROTO( // simple constructor + CHAR_SAMPLE *sample); + + ~CHAR_PROTO (); + + float match_sample(CHAR_SAMPLE *test_sample); + + float match(CHAR_PROTO *test_proto); + + INT32 n_samples() { + return nsamples; + } + + INT32 x_size() { + return xsize; + } + + INT32 y_size() { + return ysize; + } + + float **data() { + return proto; + } + char character() { + return ch; + } + + void enlarge_prototype(INT32 new_xsize, INT32 new_ysize); + + void add_sample(CHAR_SAMPLE *sample); + + IMAGE *make_image(); + + void print(FILE *f); + + NEWDELETE2 (CHAR_PROTO) private: + INT32 xsize; + INT32 ysize; + float *proto_data; + float **proto; + INT32 nsamples; + char ch; +}; +#endif diff --git a/wordrec/chop.cpp b/wordrec/chop.cpp new file mode 100644 index 0000000000..2984d88540 --- /dev/null +++ b/wordrec/chop.cpp @@ -0,0 +1,458 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: chop.c (Formerly chop.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 30 16:41:11 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "chop.h" +#include "debug.h" +#include "outlines.h" +#include "olutil.h" +#include "tordvars.h" +#include "callcpp.h" +#include "plotedges.h" +#include "const.h" + +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +make_int_var (chop_debug, 0, make_chop_debug, +3, 1, set_chop_debug, "Chop debug"); + +make_int_var (chop_enable, 1, make_chop_enable, +3, 2, set_chop_enable, "Chop enable"); + +make_toggle_var (vertical_creep, 0, make_vertical_creep, +3, 4, set_vertical_creep, "Vertical creep"); + +make_int_var (split_length, 10000, make_split_length, +3, 5, set_split_length, "Split Length"); + +make_int_var (same_distance, 2, make_same_distance, +3, 6, set_same_distance, "Same distance"); + +make_int_var (min_outline_points, 6, make_min_points, +3, 9, set_min_points, "Min Number of Points on Outline"); + +make_int_var (inside_angle, -50, make_inside_angle, +3, 12, set_inside_angle, "Min Inside Angle Bend"); + +make_int_var (min_outline_area, 2000, make_outline_area, +3, 13, set_outline_area, "Min Outline Area"); + +/*---------------------------------------------------------------------- + V a r i a b l e s (moved from gradechop) +----------------------------------------------------------------------*/ +make_float_var (split_dist_knob, 0.5, make_split_dist, +3, 17, set_split_dist, "Split length adjustment"); + +make_float_var (overlap_knob, 0.9, make_overlap_knob, +3, 18, set_overlap_knob, "Split overlap adjustment"); + +make_float_var (center_knob, 0.15, make_center_knob, +3, 19, set_center_knob, "Split center adjustment"); + +make_float_var (sharpness_knob, 0.06, make_sharpness_knob, +3, 20, set_sharpness_knob, "Split sharpness adjustment"); + +make_float_var (width_change_knob, 5.0, make_width_change, +3, 21, set_width_change_knob, "Width change adjustment"); + +make_float_var (ok_split, 100.0, make_ok_split, +3, 14, set_ok_split, "OK split limit"); + +make_float_var (good_split, 50.0, make_good_split, +3, 15, set_good_split, "Good split limit"); + +make_int_var (x_y_weight, 3, make_x_y_weight, +3, 16, set_x_y_weight, "X / Y length weight"); + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * length_product + * + * Compute the product of the length of two vectors. The + * vectors must be of type POINT. This product is used in computing + * angles. + **********************************************************************/ +#define length_product(p1,p2) \ +(sqrt ((((float) (p1).x * (p1).x + (float) (p1).y * (p1).y) * \ + ((float) (p2).x * (p2).x + (float) (p2).y * (p2).y)))) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * point_priority + * + * Assign a priority to and edge point that might be used as part of a + * split. The argument should be of type EDGEPT. + **********************************************************************/ +PRIORITY point_priority(EDGEPT *point) { + return ((PRIORITY) point_bend_angle (point)); +} + + +/********************************************************************** + * add_point_to_list + * + * Add an edge point to a POINT_GROUP containg a list of other points. + **********************************************************************/ +void add_point_to_list(POINT_GROUP point_list, EDGEPT *point) { + HEAPENTRY data; + + if (SizeOfHeap (point_list) < MAX_NUM_POINTS - 2) { + data.Data = (char *) point; + data.Key = point_priority (point); + HeapStore(point_list, &data); + } + +#ifndef GRAPHICS_DISABLED + if (chop_debug) + mark_outline(point); +#endif +} + + +/********************************************************************** + * angle_change + * + * Return the change in angle (degrees) of the line segments between + * points one and two, and two and three. + **********************************************************************/ +int angle_change(EDGEPT *point1, EDGEPT *point2, EDGEPT *point3) { + VECTOR vector1; + VECTOR vector2; + + int angle; + float length; + + /* Compute angle */ + vector1.x = point2->pos.x - point1->pos.x; + vector1.y = point2->pos.y - point1->pos.y; + vector2.x = point3->pos.x - point2->pos.x; + vector2.y = point3->pos.y - point2->pos.y; + /* Use cross product */ + length = length_product (vector1, vector2); + if ((int) length == 0) + return (0); + angle = (int) (asin (CROSS (vector1, vector2) / length) / PI * 180.0); + + /* Use dot product */ + if (SCALAR (vector1, vector2) < 0) + angle = 180 - angle; + /* Adjust angle */ + if (angle > 180) + angle -= 360; + if (angle <= -180) + angle += 360; + return (angle); +} + + +/********************************************************************** + * init_chop + * + * Create the required chopper variables. + **********************************************************************/ +void init_chop() { + make_same_distance(); + make_vertical_creep(); + make_x_y_weight(); + make_chop_enable(); + make_chop_debug(); + make_split_dist(); + make_overlap_knob(); + make_sharpness_knob(); + make_width_change(); + make_good_split(); + make_ok_split(); + make_center_knob(); + make_split_length(); + make_min_points(); + make_inside_angle(); + make_outline_area(); +} + + +/********************************************************************** + * is_little_chunk + * + * Return TRUE if one of the pieces resulting from this split would + * less than some number of edge points. + **********************************************************************/ +int is_little_chunk(EDGEPT *point1, EDGEPT *point2) { + EDGEPT *p = point1; /* Iterator */ + int counter = 0; + + do { + /* Go from P1 to P2 */ + if (is_same_edgept (point2, p)) { + if (is_small_area (point1, point2)) + return (TRUE); + else + break; + } + p = p->next; + } + while ((p != point1) && (counter++ < min_outline_points)); + /* Go from P2 to P1 */ + p = point2; + counter = 0; + do { + if (is_same_edgept (point1, p)) { + return (is_small_area (point2, point1)); + } + p = p->next; + } + while ((p != point2) && (counter++ < min_outline_points)); + + return (FALSE); +} + + +/********************************************************************** + * is_small_area + * + * Test the area defined by a split accross this outline. + **********************************************************************/ +int is_small_area(EDGEPT *point1, EDGEPT *point2) { + EDGEPT *p = point1->next; /* Iterator */ + int area = 0; + TPOINT origin; + + do { + /* Go from P1 to P2 */ + origin.x = p->pos.x - point1->pos.x; + origin.y = p->pos.y - point1->pos.y; + area += CROSS (origin, p->vec); + p = p->next; + } + while (!is_same_edgept (point2, p)); + + return (area < min_outline_area); +} + + +/********************************************************************** + * pick_close_point + * + * Choose the edge point that is closest to the critical point. This + * point may not be exactly vertical from the critical point. + **********************************************************************/ +EDGEPT *pick_close_point(EDGEPT *critical_point, + EDGEPT *vertical_point, + int *best_dist) { + EDGEPT *best_point = NULL; + int this_distance; + int found_better; + + do { + found_better = FALSE; + + this_distance = edgept_dist (critical_point, vertical_point); + if (this_distance <= *best_dist) { + + if (!(same_point (critical_point->pos, vertical_point->pos) || + same_point (critical_point->pos, vertical_point->next->pos) + || best_point != NULL + && same_point (best_point->pos, vertical_point->pos) || + is_exterior_point (critical_point, vertical_point))) { + *best_dist = this_distance; + best_point = vertical_point; + if (vertical_creep) + found_better = TRUE; + } + } + vertical_point = vertical_point->next; + } + while (found_better == TRUE); + + return (best_point); +} + + +/********************************************************************** + * prioritize_points + * + * Find a list of edge points from the outer outline of this blob. For + * each of these points assign a priority. Sort these points using a + * heap structure so that they can be visited in order. + **********************************************************************/ +void prioritize_points(TESSLINE *outline, POINT_GROUP points) { + EDGEPT *this_point; + EDGEPT *local_min = NULL; + EDGEPT *local_max = NULL; + + this_point = outline->loop; + local_min = this_point; + local_max = this_point; + do { + if (debug_5) + cprintf ("(%3d,%3d) min=%3d, max=%3d, dir=%2d, ang=%2.0f\n", + this_point->pos.x, this_point->pos.y, + (local_min ? local_min->pos.y : 999), + (local_max ? local_max->pos.y : 999), + direction (this_point), point_priority (this_point)); + + if (this_point->vec.y < 0) { + /* Look for minima */ + if (local_max != NULL) + new_max_point(local_max, points); + else if (is_inside_angle (this_point)) + add_point_to_list(points, this_point); + local_max = NULL; + local_min = this_point->next; + } + else if (this_point->vec.y > 0) { + /* Look for maxima */ + if (local_min != NULL) + new_min_point(local_min, points); + else if (is_inside_angle (this_point)) + add_point_to_list(points, this_point); + local_min = NULL; + local_max = this_point->next; + } + else { + /* Flat area */ + if (local_max != NULL) { + if (local_max->prev->vec.y != 0) { + new_max_point(local_max, points); + } + local_max = this_point->next; + local_min = NULL; + } + else { + if (local_min->prev->vec.y != 0) { + new_min_point(local_min, points); + } + local_min = this_point->next; + local_max = NULL; + } + } + + /* Next point */ + this_point = this_point->next; + } + while (this_point != outline->loop); +} + + +/********************************************************************** + * new_min_point + * + * Found a new minimum point try to decide whether to save it or not. + * Return the new value for the local minimum. If a point is saved then + * the local minimum is reset to NULL. + **********************************************************************/ +void new_min_point(EDGEPT *local_min, POINT_GROUP points) { + INT16 dir; + + dir = direction (local_min); + + if (dir < 0) { + add_point_to_list(points, local_min); + return; + } + + if (dir == 0 && point_priority (local_min) < 0) { + add_point_to_list(points, local_min); + return; + } +} + + +/********************************************************************** + * new_max_point + * + * Found a new minimum point try to decide whether to save it or not. + * Return the new value for the local minimum. If a point is saved then + * the local minimum is reset to NULL. + **********************************************************************/ +void new_max_point(EDGEPT *local_max, POINT_GROUP points) { + INT16 dir; + + dir = direction (local_max); + + if (dir > 0) { + add_point_to_list(points, local_max); + return; + } + + if (dir == 0 && point_priority (local_max) < 0) { + add_point_to_list(points, local_max); + return; + } +} + + +/********************************************************************** + * vertical_projection_point + * + * For one point on the outline, find the corresponding point on the + * other side of the outline that is a likely projection for a split + * point. This is done by iterating through the edge points until the + * X value of the point being looked at is greater than the X value of + * the split point. Ensure that the point being returned is not right + * next to the split point. Return the edge point as a result. + **********************************************************************/ +void vertical_projection_point(EDGEPT *split_point, EDGEPT *target_point, + EDGEPT** best_point) { + EDGEPT *p; /* Iterator */ + EDGEPT *this_edgept; /* Iterator */ + int x = split_point->pos.x; /* X value of vertical */ + int best_dist = LARGE_DISTANCE;/* Best point found */ + + if (*best_point != NULL) + best_dist = edgept_dist(split_point, *best_point); + + p = target_point; + /* Look at each edge point */ + do { + if ((((p->pos.x <= x) && (x <= p->next->pos.x)) || + ((p->next->pos.x <= x) && (x <= p->pos.x))) && + !same_point (split_point->pos, p->pos) && + !same_point (split_point->pos, p->next->pos) + && (*best_point == NULL || !same_point ((*best_point)->pos, p->pos))) { + + this_edgept = near_point (split_point, p, p->next); + + if (*best_point == NULL) + best_dist = edgept_dist (split_point, this_edgept); + + this_edgept = + pick_close_point(split_point, this_edgept, &best_dist); + if (this_edgept) + *best_point = this_edgept; + } + + p = p->next; + } + while (p != target_point); +} diff --git a/wordrec/chop.h b/wordrec/chop.h new file mode 100644 index 0000000000..9f278b7e82 --- /dev/null +++ b/wordrec/chop.h @@ -0,0 +1,153 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: chop.h (Formerly chop.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Wed Jul 10 14:47:37 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *******************************************************************************/ + +#ifndef CHOP_H +#define CHOP_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "oldheap.h" +#include "seam.h" + +/*---------------------------------------------------------------------- + T y p e s +---------------------------------------------------------------------*/ +#define MAX_NUM_POINTS 50 +typedef HEAP *POINT_GROUP; +typedef HEAP *SPLIT_GROUP; + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int chop_enable; +extern int chop_debug; +extern int same_distance; +extern int split_length; +extern float center_knob; +extern float overlap_knob; +extern float split_dist_knob; +extern float width_change_knob; +extern float sharpness_knob; +extern int min_outline_area; +extern int min_outline_points; +extern float good_split; +extern float ok_split; +extern int chop_enable; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * point_bend_angle + * + * Measure the angle of bend at this edge point. The argument should + * be of type EDGEPT. + **********************************************************************/ +#define point_bend_angle(point) \ +(angle_change ((point)->prev, (point), (point)->next)) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +PRIORITY point_priority(EDGEPT *point); + +void add_point_to_list(POINT_GROUP point_list, EDGEPT *point); + +int angle_change(EDGEPT *point1, EDGEPT *point2, EDGEPT *point3); + +void init_chop(); + +int is_little_chunk(EDGEPT *point1, EDGEPT *point2); + +int is_small_area(EDGEPT *point1, EDGEPT *point2); + +EDGEPT *pick_close_point(EDGEPT *critical_point, + EDGEPT *vertical_point, + int *best_dist); + +void prioritize_points(TESSLINE *outline, POINT_GROUP points); + +void new_min_point(EDGEPT *local_min, POINT_GROUP points); + +void new_max_point(EDGEPT *local_max, POINT_GROUP points); + +void vertical_projection_point(EDGEPT *split_point, EDGEPT *target_point, + EDGEPT** best_point); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* chop.c +PRIORITY point_priority + _ARGS((EDGEPT *point)); + +void add_point_to_list + _ARGS((POINT_GROUP point_list, + EDGEPT *point)); + +SEAM *create_split + _ARGS((BLOB *blob, + POINT_GROUP points)); + +SPLIT *extended_split + _ARGS((INT32 location, + EDGEPT *starting_point)); + +SEAM *get_best_pair + _ARGS((SPLIT_GROUP split_queue, + BLOB *blob)); + +void init_chop + _ARGS((void)); + +EDGEPT *pick_close_point + _ARGS((EDGEPT *critical_point, + EDGEPT *vertical_point, + int *best_dist)); + +void prioritize_points + _ARGS((TESSLINE *outline, + POINT_GROUP points)); + +void new_min_point + _ARGS((EDGEPT *local_min, + POINT_GROUP points)); + +void new_max_point + _ARGS((EDGEPT *local_max, + POINT_GROUP points)); + +EDGEPT *vertical_projection_point + _ARGS((EDGEPT *split_point, + EDGEPT *target_point)); + +#undef _ARGS +*/ +#endif diff --git a/wordrec/chopper.cpp b/wordrec/chopper.cpp new file mode 100644 index 0000000000..88927c2933 --- /dev/null +++ b/wordrec/chopper.cpp @@ -0,0 +1,739 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: chopper.c (Formerly chopper.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 30 16:18:52 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **************************************************************************/ + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "chopper.h" +#include "wordclass.h" +#include "makechop.h" +#include "associate.h" +#include "metrics.h" +#include "tordvars.h" +#include "stopper.h" +#include "callcpp.h" +#include "structures.h" +#include "findseam.h" +#include "render.h" +#include "seam.h" +#include "const.h" +#include "freelist.h" +#include "pieces.h" +#include "permute.h" +//#include "tessvars.h" + +#include + +extern int blob_skip; +INT_VAR (repair_unchopped_blobs, 1, "Fix blobs that aren't chopped"); + +//?extern int tessedit_dangambigs_chop; +double_VAR (tessedit_certainty_threshold, -2.25, "Good blob limit"); + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * bounds_inside + * + * Check to see if the bounding box of one thing is inside the + * bounding box of another. + **********************************************************************/ +#define bounds_inside(inner_tl,inner_br,outer_tl,outer_br) \ +((inner_tl.x >= outer_tl.x) && \ +(inner_tl.y <= outer_tl.y) && \ +(inner_br.x <= outer_br.x) && \ +(inner_br.y >= outer_br.y)) \ + +/********************************************************************** + * set_null_choice + * + * Set the fields in this choice to be defaulted bad initial values. + **********************************************************************/ +#define set_null_choice(choice) \ +(class_string (choice) = NULL, \ +class_probability (choice) = MAX_FLOAT32, \ +class_certainty (choice) = -MAX_FLOAT32) \ + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * preserve_outline_tree + * + * Copy the list of outlines. + **********************************************************************/ +void preserve_outline(EDGEPT *start) { + EDGEPT *srcpt; + + if (start == NULL) + return; + srcpt = start; + do { + srcpt->flags[1] = 1; + srcpt = srcpt->next; + } + while (srcpt != start); + srcpt->flags[1] = 2; +} + + +/**************************************************************************/ +void preserve_outline_tree(TESSLINE *srcline) { + TESSLINE *outline; + + for (outline = srcline; outline != NULL; outline = outline->next) { + preserve_outline (outline->loop); + } + if (srcline->child != NULL) + preserve_outline_tree (srcline->child); +} + + +/********************************************************************** + * restore_outline_tree + * + * Copy the list of outlines. + **********************************************************************/ +EDGEPT *restore_outline(EDGEPT *start) { + EDGEPT *srcpt; + EDGEPT *real_start; + EDGEPT *deadpt; + + if (start == NULL) + return NULL; + srcpt = start; + do { + if (srcpt->flags[1] == 2) + break; + srcpt = srcpt->next; + } + while (srcpt != start); + real_start = srcpt; + do { + if (srcpt->flags[1] == 0) { + deadpt = srcpt; + srcpt = srcpt->next; + srcpt->prev = deadpt->prev; + deadpt->prev->next = srcpt; + deadpt->prev->vec.x = srcpt->pos.x - deadpt->prev->pos.x; + deadpt->prev->vec.y = srcpt->pos.y - deadpt->prev->pos.y; + oldedgept(deadpt); + } + else + srcpt = srcpt->next; + } + while (srcpt != real_start); + return real_start; +} + + +/******************************************************************************/ +void restore_outline_tree(TESSLINE *srcline) { + TESSLINE *outline; + + for (outline = srcline; outline != NULL; outline = outline->next) { + outline->loop = restore_outline (outline->loop); + outline->start = outline->loop->pos; + } + if (srcline->child != NULL) + restore_outline_tree (srcline->child); +} + + +/********************************************************************** + * attempt_blob_chop + * + * Try to split the this blob after this one. Check to make sure that + * it was successful. + **********************************************************************/ +SEAM *attempt_blob_chop(TWERD *word, INT32 blob_number, SEAMS seam_list) { + TBLOB *blob; + TBLOB *other_blob; + SEAM *seam; + TBLOB *last_blob; + TBLOB *next_blob; + INT16 x; + + if (first_pass) + chops_attempted1++; + else + chops_attempted2++; + + last_blob = NULL; + blob = word->blobs; + for (x = 0; x < blob_number; x++) { + last_blob = blob; + blob = blob->next; + } + next_blob = blob->next; + + if (repair_unchopped_blobs) + preserve_outline_tree (blob->outlines); + other_blob = newblob (); /* Make new blob */ + other_blob->next = blob->next; + other_blob->outlines = NULL; + blob->next = other_blob; + + seam = pick_good_seam (blob); + if (chop_debug) { + if (seam != NULL) { + print_seam ("Good seam picked=", seam); + } + else + cprintf ("\n** no seam picked *** \n"); + } + if (seam) { + apply_seam(blob, other_blob, seam); + } + + if ((seam == NULL) || + (blob->outlines == NULL) || + (other_blob->outlines == NULL) || + total_containment (blob, other_blob) || + check_blob (other_blob) || + !(check_seam_order (blob, seam) && + check_seam_order (other_blob, seam)) || + any_shared_split_points (seam_list, seam) || + !test_insert_seam(seam_list, blob_number, blob, word->blobs)) { + + blob->next = next_blob; + if (seam) { + undo_seam(blob, other_blob, seam); + delete_seam(seam); +#ifndef GRAPHICS_DISABLED + if (chop_debug) { + display_blob(blob, Red); + cprintf ("\n** seam being removed ** \n"); + } +#endif + } + else { + oldblob(other_blob); + } + + if (repair_unchopped_blobs) + restore_outline_tree (blob->outlines); + return (NULL); + } + return (seam); +} + + +/********************************************************************** + * any_shared_split_points + * + * Return true if any of the splits share a point with this one. + **********************************************************************/ +int any_shared_split_points(SEAMS seam_list, SEAM *seam) { + int length; + int index; + + length = array_count (seam_list); + for (index = 0; index < length; index++) + if (shared_split_points ((SEAM *) array_value (seam_list, index), seam)) + return TRUE; + return FALSE; +} + + +/********************************************************************** + * check_blob + * + * Return true if blob has a non whole outline. + **********************************************************************/ +int check_blob(TBLOB *blob) { + TESSLINE *outline; + EDGEPT *edgept; + + for (outline = blob->outlines; outline != NULL; outline = outline->next) { + edgept = outline->loop; + do { + if (edgept == NULL) + break; + edgept = edgept->next; + } + while (edgept != outline->loop); + if (edgept == NULL) + return 1; + } + return 0; +} + + +/********************************************************************** + * improve_one_blob + * + * Start with the current word of blobs and its classification. Find + * the worst blobs and try to divide it up to improve the ratings. + *********************************************************************/ +CHOICES_LIST improve_one_blob(TWERD *word, + CHOICES_LIST char_choices, + int fx, + INT32 *blob_number, + SEAMS *seam_list, + DANGERR *fixpt, + STATE *this_state, + STATE *correct_state, + INT32 pass) { + TBLOB *pblob; + TBLOB *blob; + INT16 x = 0; + float rating_ceiling = MAX_FLOAT32; + CHOICES answer; + SEAM *seam; + + do { + *blob_number = select_blob_to_split (char_choices, rating_ceiling); + if (*blob_number == -1) + return (NULL); + + seam = attempt_blob_chop (word, *blob_number, *seam_list); + if (seam != NULL) + break; + /* Must split null blobs */ + answer = (CHOICES) array_value (char_choices, *blob_number); + if (answer == NIL) + return (NULL); /* Try different blob */ + rating_ceiling = best_probability (answer); + } + while (!blob_skip); + /* Split OK */ + for (blob = word->blobs, pblob = NULL; x < *blob_number; x++) { + pblob = blob; + blob = blob->next; + } + + *seam_list = + insert_seam (*seam_list, *blob_number, seam, blob, word->blobs); + + free_choices ((CHOICES) array_value (char_choices, *blob_number)); + + answer = + classify_blob (pblob, blob, blob->next, NULL, fx, "improve 1:", Red, + this_state, correct_state, pass, *blob_number); + char_choices = array_insert (char_choices, *blob_number, answer); + + answer = + classify_blob (blob, blob->next, blob->next->next, NULL, fx, "improve 2:", + Yellow, this_state, correct_state, pass, *blob_number + 1); + array_value (char_choices, *blob_number + 1) = (char *) answer; + + return (char_choices); +} + + +/********************************************************************** + * check_seam_order + * + * Make sure that each of the splits in this seam match to outlines + * in this blob. If any of the splits could not correspond to this + * blob then there is a problem (and FALSE should be returned to the + * caller). + **********************************************************************/ +INT16 check_seam_order(TBLOB *blob, SEAM *seam) { + TESSLINE *outline; + TESSLINE *last_outline; + INT8 found_em[3]; + + if (seam->split1 == NULL || seam->split1 == NULL || blob == NULL) + return (TRUE); + + found_em[0] = found_em[1] = found_em[2] = FALSE; + + for (outline = blob->outlines; outline; outline = outline->next) { + if (!found_em[0] && + ((seam->split1 == NULL) || + is_split_outline (outline, seam->split1))) { + found_em[0] = TRUE; + } + if (!found_em[1] && + ((seam->split2 == NULL) || + is_split_outline (outline, seam->split2))) { + found_em[1] = TRUE; + } + if (!found_em[2] && + ((seam->split3 == NULL) || + is_split_outline (outline, seam->split3))) { + found_em[2] = TRUE; + } + last_outline = outline; + } + + if (!found_em[0] || !found_em[1] || !found_em[2]) + return (FALSE); + else + return (TRUE); +} + + +/********************************************************************** + * chop_word_main + * + * Classify the blobs in this word and permute the results. Find the + * worst blob in the word and chop it up. Continue this process until + * a good answer has been found or all the blobs have been chopped up + * enough. Return the word level ratings. + **********************************************************************/ +CHOICES_LIST chop_word_main(register TWERD *word, + int fx, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + BOOL8 tester, + BOOL8 trainer) { + TBLOB *pblob; + TBLOB *blob; + CHOICES_LIST char_choices; + int index; + int did_chopping; + float rating_limit = 1000.0; + STATE state; + SEAMS seam_list = NULL; + CHOICES match_result; + MATRIX ratings = NULL; + DANGERR fixpt; /*dangerous ambig */ + INT32 state_count; //no of states + INT32 bit_count; //no of bits + static STATE best_state; + static STATE chop_states[64]; //in between states + + state_count = 0; + set_null_choice(best_choice); + set_null_choice(raw_choice); + + char_choices = new_choice_list (); + + did_chopping = 0; + for (blob = word->blobs, pblob = NULL, index = 0; blob != NULL; + blob = blob->next, index++) { + match_result = + (CHOICES) classify_blob (pblob, blob, blob->next, NULL, fx, + "chop_word:", Green, &chop_states[0], + &best_state, matcher_pass, index); + char_choices = array_push (char_choices, match_result); + pblob = blob; + } + bit_count = index - 1; + permute_characters(char_choices, rating_limit, best_choice, raw_choice); + + set_n_ones (&state, array_count (char_choices) - 1); + if (matcher_fp != NULL) { + if (matcher_pass == 0) { + bits_in_states = bit_count; + chop_states[state_count] = state; + } + state_count++; + } + + if (!AcceptableChoice (char_choices, best_choice, raw_choice, &fixpt) + || (tester || trainer) + && strcmp (word->correct, class_string (best_choice))) { + did_chopping = 1; + if (first_pass) + words_chopped1++; + else + words_chopped2++; + + seam_list = start_seam_list (word->blobs); + + if (chop_enable) + improve_by_chopping(word, + &char_choices, + fx, + &state, + best_choice, + raw_choice, + &seam_list, + &fixpt, + chop_states, + &state_count, + &best_state, + matcher_pass); + + if (chop_debug) + print_seams ("Final seam list:", seam_list); + + if (enable_assoc && + !AcceptableChoice (char_choices, best_choice, raw_choice, NULL) + || (tester || trainer) + && strcmp (word->correct, class_string (best_choice))) { + ratings = word_associator (word->blobs, seam_list, &state, fx, + best_choice, raw_choice, word->correct, + /*0, */ &fixpt, + &best_state, matcher_pass); + } + bits_in_states = bit_count + state_count - 1; + + } + if (ratings != NULL) + free_matrix(ratings); + if (did_chopping || tester || trainer) + char_choices = rebuild_current_state (word->blobs, seam_list, &state, + char_choices, fx); + if (seam_list != NULL) + free_seam_list(seam_list); + if (matcher_fp != NULL) { + best_state = state; + } + FilterWordChoices(); + return char_choices; +} + + +/********************************************************************** + * improve_by_chopping + * + * Start with the current word of blobs and its classification. Find + * the worst blobs and try to divide them up to improve the ratings. + * As long as ratings are produced by the new blob splitting. When + * all the splitting has been accomplished all the ratings memory is + * reclaimed. + **********************************************************************/ +void improve_by_chopping(register TWERD *word, + CHOICES_LIST *char_choices, + int fx, + STATE *best_state, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + SEAMS *seam_list, + DANGERR *fixpt, + STATE *chop_states, + INT32 *state_count, + STATE *correct_state, + INT32 pass) { + INT32 blob_number; + INT32 index; //to states + CHOICES_LIST choices = *char_choices; + float old_best; + int fixpt_valid = 1; + static INT32 old_count; //from pass1 + + do { + /* Improvement loop */ + if (!fixpt_valid) + fixpt->index = -1; + old_best = class_probability (best_choice); + choices = improve_one_blob (word, *char_choices, fx, + &blob_number, seam_list, fixpt, + chop_states + *state_count, correct_state, + pass); + if (choices != NULL) { + LogNewSplit(blob_number); + permute_characters (choices, + class_probability (best_choice), + best_choice, raw_choice); + *char_choices = choices; + + if (old_best > class_probability (best_choice)) { + set_n_ones (best_state, array_count (*char_choices) - 1); + fixpt_valid = 1; + } + else { + insert_new_chunk (best_state, blob_number, + array_count (*char_choices) - 2); + fixpt_valid = 0; + } + if (*state_count > 0) { + if (pass == 0) { + for (index = 0; index < *state_count; index++) + insert_new_chunk (&chop_states[index], blob_number, + array_count (*char_choices) - 2); + set_n_ones (&chop_states[index], + array_count (*char_choices) - 1); + } + (*state_count)++; + } + + if (chop_debug) + print_state ("best state = ", + best_state, count_blobs (word->blobs) - 1); + if (first_pass) + chops_performed1++; + else + chops_performed2++; + + } + } + while (choices && + !AcceptableChoice (*char_choices, best_choice, raw_choice, fixpt) && + !blob_skip && array_count (*char_choices) < MAX_NUM_CHUNKS); + if (pass == 0) + old_count = *state_count; + else { + if (old_count != *state_count) + fprintf (matcher_fp, + "Mis-matched state counts, " INT32FORMAT " pass1, " + INT32FORMAT " pass2\n", old_count, *state_count); + } + if (!fixpt_valid) + fixpt->index = -1; +} + + +/********************************************************************** + * select_blob_to_split + * + * These are the results of the last classification. Find a likely + * place to apply splits. + **********************************************************************/ +INT16 select_blob_to_split(CHOICES_LIST char_choices, float rating_ceiling) { + CHOICES this_choice; + int x; + float worst = -MAX_FLOAT32; + int worst_index = -1; + + if (chop_debug) + if (rating_ceiling < MAX_FLOAT32) + cprintf ("rating_ceiling = %8.4f\n", rating_ceiling); + else + cprintf ("rating_ceiling = No Limit\n"); + + for_each_choice(char_choices, x) { + this_choice = (CHOICES) array_value (char_choices, x); + if (this_choice == NIL) { + return (x); + } + else { + if (best_probability (this_choice) > worst && + best_probability (this_choice) < rating_ceiling && + best_certainty (this_choice) < tessedit_certainty_threshold) { + worst_index = x; + worst = best_probability (this_choice); + } + } + } + + if (chop_debug) + cprintf ("blob_number = %4d\n", worst_index); + + return (worst_index); +} + + +/********************************************************************** + * start_seam_list + * + * Initialize a list of seams that match the original number of blobs + * present in the starting segmentation. Each of the seams created + * by this routine have location information only. + **********************************************************************/ +SEAMS start_seam_list(TBLOB *blobs) { + TBLOB *blob; + SEAMS seam_list; + TPOINT topleft; + TPOINT botright; + int location; + /* Seam slot per char */ + seam_list = new_seam_list (); + + for (blob = blobs; blob->next != NULL; blob = blob->next) { + + blob_bounding_box(blob, &topleft, &botright); + location = botright.x; + blob_bounding_box (blob->next, &topleft, &botright); + location += topleft.x; + location /= 2; + + seam_list = add_seam (seam_list, + new_seam (0.0, location, NULL, NULL, NULL)); + } + + return (seam_list); +} + + +/********************************************************************** + * total_containment + * + * Check to see if one of these outlines is totally contained within + * the bounding box of the other. + **********************************************************************/ +INT16 total_containment(TBLOB *blob1, TBLOB *blob2) { + TPOINT topleft1; + TPOINT botright1; + TPOINT topleft2; + TPOINT botright2; + + blob_bounding_box(blob1, &topleft1, &botright1); + blob_bounding_box(blob2, &topleft2, &botright2); + + return (bounds_inside (topleft1, botright1, topleft2, botright2) || + bounds_inside (topleft2, botright2, topleft1, botright1)); +} + + +/********************************************************************** + * word_associator + * + * Reassociate and classify the blobs in a word. Continue this process + * until a good answer is found or all the possibilities have been tried. + **********************************************************************/ +MATRIX word_associator(TBLOB *blobs, + SEAMS seams, + STATE *state, + int fxid, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + char *correct, + DANGERR *fixpt, + STATE *best_state, + INT32 pass) { + CHUNKS_RECORD chunks_record; + BLOB_WEIGHTS blob_weights; + int x; + int num_chunks; + A_CHOICE *this_choice; + + num_chunks = array_count (seams) + 1; + + chunks_record.chunks = blobs; + chunks_record.splits = seams; + chunks_record.ratings = record_piece_ratings (blobs); + chunks_record.char_widths = blobs_widths (blobs); + chunks_record.chunk_widths = blobs_widths (blobs); + chunks_record.fx = fxid; + /* Save chunk weights */ + for (x = 0; x < num_chunks; x++) { + this_choice = + (A_CHOICE *) first (matrix_get (chunks_record.ratings, x, x)); + blob_weights[x] = -(INT16) (10 * class_probability (this_choice) / + class_certainty (this_choice)); + } + chunks_record.weights = blob_weights; + + if (chop_debug) + print_matrix (chunks_record.ratings); + best_first_search(&chunks_record, + best_choice, + raw_choice, + state, + fixpt, + best_state, + pass); + + free_widths (chunks_record.chunk_widths); + free_widths (chunks_record.char_widths); + return chunks_record.ratings; +} diff --git a/wordrec/chopper.h b/wordrec/chopper.h new file mode 100644 index 0000000000..61dbcb4d1a --- /dev/null +++ b/wordrec/chopper.h @@ -0,0 +1,104 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: chopper.h (Formerly chopper.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Wed May 15 14:24:26 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ + +#ifndef CHOPPER_H +#define CHOPPER_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "choicearr.h" +#include "seam.h" +#include "matrix.h" +#include "stopper.h" +#include "states.h" +#include "cutil.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void preserve_outline(EDGEPT *start); + +void preserve_outline_tree(TESSLINE *srcline); + +EDGEPT *restore_outline(EDGEPT *start); + +void restore_outline_tree(TESSLINE *srcline); + +SEAM *attempt_blob_chop(TWERD *word, INT32 blob_number, SEAMS seam_list); + +int any_shared_split_points(SEAMS seam_list, SEAM *seam); + +int check_blob(TBLOB *blob); + +CHOICES_LIST improve_one_blob(TWERD *word, + CHOICES_LIST char_choices, + int fx, + INT32 *blob_number, + SEAMS *seam_list, + DANGERR *fixpt, + STATE *this_state, + STATE *correct_state, + INT32 pass); + +INT16 check_seam_order(TBLOB *blob, SEAM *seam); + +CHOICES_LIST chop_word_main(register TWERD *word, + int fx, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + BOOL8 tester, + BOOL8 trainer); + +void improve_by_chopping(register TWERD *word, + CHOICES_LIST *char_choices, + int fx, + STATE *best_state, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + SEAMS *seam_list, + DANGERR *fixpt, + STATE *chop_states, + INT32 *state_count, + STATE *correct_state, + INT32 pass); + +INT16 select_blob_to_split(CHOICES_LIST char_choices, float rating_ceiling); + +SEAMS start_seam_list(TBLOB *blobs); + +INT16 total_containment(TBLOB *blob1, TBLOB *blob2); + +MATRIX word_associator(TBLOB *blobs, + SEAMS seams, + STATE *state, + int fxid, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + char *correct, + DANGERR *fixpt, + STATE *best_state, + INT32 pass); +#endif diff --git a/wordrec/closed.cpp b/wordrec/closed.cpp new file mode 100644 index 0000000000..6e51e0cbc4 --- /dev/null +++ b/wordrec/closed.cpp @@ -0,0 +1,136 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: closed.c (Formerly closed.c) + * Description: Hash table for closed search states. + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Fri May 25 11:31:16 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "freelist.h" +#include "closed.h" +#include "cutil.h" +#include "callcpp.h" +//#include +#ifdef __UNIX__ +#include +#endif + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +#define TABLE_SIZE 2000 +HASH_TABLE global_hash = NULL; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * hash_add + * + * Look in the hash table for a particular value. If it is not there + * then add it. + **********************************************************************/ +int hash_add(HASH_TABLE state_table, STATE *state) { + int x; + int i = 0; + int table_limit = TABLE_SIZE; + + x = state->part2 % table_limit; + while (i < table_limit) { + assert (0 <= x && x < table_limit); + /* Found it */ + if ((state_table[x].part2 == state->part2) && + (state_table[x].part1 == state->part1)) { + return (FALSE); + } + /* Not in table */ + else if (state_table[x].part1 == NO_STATE) { + state_table[x].part2 = state->part2; + state_table[x].part1 = state->part1; + return (TRUE); + } + i++; + if (++x >= table_limit) + x = 0; + } + cprintf("warning: hash table is full"); + + abort(); + return 0; +} + + +/********************************************************************** + * hash_lookup + * + * Look in the hash table for a particular value. If the value is there + * then return TRUE, FALSE otherwise. + **********************************************************************/ +int hash_lookup(HASH_TABLE state_table, STATE *state) { + int x; + int i = 0; + int table_limit = TABLE_SIZE; + + x = state->part2 % table_limit; + while (i < table_limit) { + assert (0 <= x && x < table_limit); + /* Found it */ + if ((state_table[x].part2 == state->part2) && + (state_table[x].part1 == state->part1)) { + return (TRUE); + } + /* Not in table */ + else if (state_table[x].part1 == NO_STATE) { + return (FALSE); + } + + i++; + if (++x >= table_limit) + x = 0; + } + cprintf ("warning: fell off end of hash table (%x) %x\n", + state->part2, state->part2 % table_limit); + abort(); + return 0; +} + + +/********************************************************************** + * new_hash_table + * + * Create and initialize a hash table. + **********************************************************************/ +HASH_TABLE new_hash_table() { + HASH_TABLE ht; + int x; + + if (global_hash == NULL) + ht = (HASH_TABLE) memalloc (TABLE_SIZE * sizeof (STATE)); + else + ht = global_hash; + + for (x = 0; x < TABLE_SIZE; x++) { + ht[x].part1 = NO_STATE; + ht[x].part2 = NO_STATE; + } + return (ht); +} diff --git a/wordrec/closed.h b/wordrec/closed.h new file mode 100644 index 0000000000..6282877f1b --- /dev/null +++ b/wordrec/closed.h @@ -0,0 +1,65 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: closed.h (Formerly closed.h) + * Description: Hash table for closed search states. + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Fri May 25 11:27:11 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ + +#ifndef CLOSED_H +#define CLOSED_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include +#include "states.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef STATE *HASH_TABLE; +#define NO_STATE ~0 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern HASH_TABLE global_hash; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * free_hash_table + * + * Free the memory taken by a state variable. + **********************************************************************/ +#define free_hash_table(table) \ + global_hash = table + +/*--------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +int hash_add(HASH_TABLE state_table, STATE *state); + +int hash_lookup(HASH_TABLE state_table, STATE *state); + +HASH_TABLE new_hash_table(); +#endif diff --git a/wordrec/djmenus.cpp b/wordrec/djmenus.cpp new file mode 100644 index 0000000000..c84d02b477 --- /dev/null +++ b/wordrec/djmenus.cpp @@ -0,0 +1,118 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: djmenus.c (Formerly djmenus.c) + * Description: Menu creation and initialization + * Author: Mark Seaman, OCR Technology + * Created: Thu Jul 27 08:59:01 1989 + * Modified: Fri Jul 12 13:33:29 1991 (Dan Johnson) danj@hpgrlj + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include +#include +#include "djmenus.h" +#include "intmatcher.h" +#include "adaptmatch.h" +#include "badwords.h" +#include "sigmenu.h" +#include "mfoutline.h" +#include "normmatch.h" +#include "speckle.h" +#include "stopper.h" + +#ifndef GRAPHICS_DISABLED +/*--------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +int handle_menu_16(); +int handle_menu_17(); +int handle_menu_18(); +int handle_menu_19(); +#endif + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * dj_cleanup + * + * Call at end of program to clean up + **********************************************************************/ +void dj_cleanup() { + EndAdaptiveClassifier(); +} + + +/********************************************************************** + * dj_statistics + * + * Call at end of program to print out statistics gathered + **********************************************************************/ +void dj_statistics(FILE *File) { + PrintAdaptiveStatistics(File); + PrintBadWords(File); +} + + +/********************************************************************** + * init_dj_debug + * + * Initialize the traps for handling the splitter debug stuff. + **********************************************************************/ +void init_dj_debug() { + static int first_time = 1; + + if (first_time) { + first_time = 0; + /* Set up the interrupts */ + #ifndef GRAPHICS_DISABLED + #ifndef SECURE_NAMES + AddSignalMenuItem (SIGINT, 16, "Int Matcher Menu ...", + handle_menu_16); + AddSignalMenuItem (SIGINT, 17, "Stopping Criterion ...", + handle_menu_17); + AddSignalMenuItem (SIGINT, 18, "Adaptive Matcher ...", + handle_menu_18); + AddSignalMenuItem (SIGINT, 19, "Word Spacing ...", + handle_menu_19); + #endif + #endif + InitAdaptiveClassifierVars(); + InitMFOutlineVars(); + InitNormProtoVars(); + InitIntProtoVars(); + InitIntegerMatcherVars(); + InitSpeckleVars(); + InitStopperVars(); + } +} + + +/********************************************************************** + * handle_menu_X + * + * Initialize the traps for handling the debug stuff. + **********************************************************************/ +#ifndef GRAPHICS_DISABLED +handle_menu (16, handle_menu_16) +handle_menu (17, handle_menu_17) +handle_menu (18, handle_menu_18) handle_menu (19, handle_menu_19) +#endif diff --git a/wordrec/djmenus.h b/wordrec/djmenus.h new file mode 100644 index 0000000000..3ddbed2642 --- /dev/null +++ b/wordrec/djmenus.h @@ -0,0 +1,33 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: djmenus.h (Formerly djmenus.h) + * Description: Create and initialize menus + * Author: Mark Seaman, OCR Technology + * Created: Mon Sep 24 09:34:21 1990 + * Modified: Thu Apr 18 14:56:19 1991 (Dan Johnson) danj@hpgrlj + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ********************************************************************************/ +#ifndef DJMENUS_H +#define DJMENUS_H + +void dj_cleanup(); + +void dj_statistics(FILE *File); + +void init_dj_debug(); +#endif diff --git a/wordrec/drawfx.cpp b/wordrec/drawfx.cpp new file mode 100644 index 0000000000..f34ee51ff6 --- /dev/null +++ b/wordrec/drawfx.cpp @@ -0,0 +1,96 @@ +/********************************************************************** + * File: drawfx.cpp (Formerly drawfx.c) + * Description: Draw things to do with feature extraction. + * Author: Ray Smith + * Created: Mon Jan 27 11:02:16 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "debugwin.h" +#include "werd.h" +#include "drawfx.h" + +#define FXDEMOWIN "FXDemo" +#define FXDEMOXPOS 250 +#define FXDEMOYPOS 0 +#define FXDEMOXSIZE 400 +#define FXDEMOYSIZE 150 +#define BLN_MAX 512 //max coord for bln +#define WERDWIDTH (BLN_MAX*20) +#define DECENT_WERD_WIDTH (5*bln_x_height) + //title of window +#define DEBUG_WIN_NAME "FXDebug" +#define DEBUG_XPOS 0 +#define DEBUG_YPOS 120 +#define DEBUG_XSIZE 80 +#define DEBUG_YSIZE 32 +#define YMAX 3508 +#define XMAX 2550 +#define MAXEDGELENGTH 1024 //max steps inoutline + +#define EXTERN + +EXTERN STRING_VAR (fx_debugfile, DEBUG_WIN_NAME, "Name of debugfile"); + +EXTERN WINDOW fx_win = NO_WINDOW; +EXTERN FILE *fx_debug = NULL; + +/********************************************************************** + * create_fx_win + * + * Create the fx window used to show the fit. + **********************************************************************/ + +void create_fx_win() { //make features win + fx_win = create_window (FXDEMOWIN, SCROLLINGWIN, + FXDEMOXPOS, FXDEMOYPOS, FXDEMOXSIZE, FXDEMOYSIZE, + -WERDWIDTH, WERDWIDTH, -BLN_MAX, BLN_MAX, + TRUE, FALSE, TRUE, TRUE); + vdc_extent (fx_win, -DECENT_WERD_WIDTH, + bln_baseline_offset - bln_x_height, + DECENT_WERD_WIDTH, 2 * bln_x_height + bln_baseline_offset); +} + + +/********************************************************************** + * clear_fx_win + * + * Clear the fx window and draw on the base/mean lines. + **********************************************************************/ + +void clear_fx_win() { //make features win + clear_view_surface(fx_win); + line_color_index(fx_win, DIM_GREY); + move2d (fx_win, -WERDWIDTH, bln_baseline_offset); + draw2d(fx_win, WERDWIDTH, bln_baseline_offset); + move2d (fx_win, -WERDWIDTH, bln_x_height + bln_baseline_offset); + draw2d (fx_win, WERDWIDTH, bln_x_height + bln_baseline_offset); +} + + +/********************************************************************** + * create_fxdebug_win + * + * Create the fx window used to show the fit. + **********************************************************************/ + +void create_fxdebug_win() { //make gradients win + // if (strcmp(fx_debugfile.string(),DEBUG_WIN_NAME)==0) + // fx_debug=create_debug_window(fx_debugfile.string(), + // DEBUG_XPOS,DEBUG_YPOS, + // DEBUG_XSIZE,DEBUG_YSIZE); + // else + // fx_debug=fopen(fx_debugfile.string(),"w"); +} diff --git a/wordrec/drawfx.h b/wordrec/drawfx.h new file mode 100644 index 0000000000..ae8d4401de --- /dev/null +++ b/wordrec/drawfx.h @@ -0,0 +1,33 @@ +/********************************************************************** + * File: drawfx.h (Formerly drawfx.h) + * Description: Draw things to do with feature extraction. + * Author: Ray Smith + * Created: Mon Jan 27 11:02:16 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef DRAWFX_H +#define DRAWFX_H + +#include "grphics.h" +#include "varable.h" +#include "notdll.h" + +extern STRING_VAR_H (fx_debugfile, DEBUG_WIN_NAME, "Name of debugfile"); +extern WINDOW fx_win; +extern FILE *fx_debug; +void create_fx_win(); //make features win +void clear_fx_win(); //make features win +void create_fxdebug_win(); //make gradients win +#endif diff --git a/wordrec/findseam.cpp b/wordrec/findseam.cpp new file mode 100644 index 0000000000..94c5eb6354 --- /dev/null +++ b/wordrec/findseam.cpp @@ -0,0 +1,554 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: findseam.c (Formerly findseam.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 30 15:44:59 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "findseam.h" +#include "gradechop.h" +#include "olutil.h" +#include "plotedges.h" +#include "outlines.h" +#include "freelist.h" +#include "seam.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define SPLIT_CLOSENESS 20/* Difference in x value */ + /* How many to keep */ +#define MAX_NUM_SEAMS 150 + /* How many to keep */ +#define MAX_OLD_SEAMS 150 +#define NO_FULL_PRIORITY -1/* Special marker for pri. */ + /* Evalute right away */ +#define GOOD_PARTIAL_PRIORITY good_split +#define BAD_PRIORITY 9999.0 + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * add_seam_to_queue + * + * Add this seam value to the seam queue. If the heap is already full + * then nothing is done. + **********************************************************************/ + +#define add_seam_to_queue(seams,seam,priority) \ +if (seam)\ +{\ + if (HeapFull(seams))\ + junk_worst_seam(seams,seam,priority);\ + else\ + HeapPush (seams, priority, (char*) seam);\ + } + +/********************************************************************** + * best_seam_priority + * + * Return the best priority value on the queue. + **********************************************************************/ + +#define best_seam_priority(seam_queue) \ +(HeapEmpty (seam_queue) ? \ + NO_FULL_PRIORITY : \ + ((SEAM*) seam_queue_element(seam_queue, 0))->priority) + +/********************************************************************** + * create_seam_queue + * + * Create a new seam queue with no elements in it. + **********************************************************************/ + +#define create_seam_queue(seam_queue) \ +(seam_queue = MakeHeap (MAX_NUM_SEAMS)) + +/********************************************************************** + * create_seam_pile + * + * Create a new seam pile with no elements in it. + **********************************************************************/ + +#define create_seam_pile(seam_pile) \ +(seam_pile = array_new (MAX_OLD_SEAMS)) + +/********************************************************************** + * delete_seam_queue + * + * Delete a seam queue along with all the seam structures associated + * with it. + **********************************************************************/ + +#define delete_seam_queue(seam_queue) \ +(FreeHeapData (seam_queue, delete_seam), \ + seam_queue = NULL) \ + + +/********************************************************************** + * pop_next_seam + * + * Remove the next seam from the queue. Put the seam and priority + * values in the requested variables. If there was nothing to pop + * then return FALSE, else return TRUE. + **********************************************************************/ + +#define pop_next_seam(seams,seam,priority) \ +(HeapPop (seams,&priority,&seam) == OK) \ + + +/********************************************************************** + * seam_queue_element + * + * Return the element from the seam queue at the requested index. + **********************************************************************/ + +#define seam_queue_element(seam_queue,index) \ +((index < SizeOfHeap (seam_queue)) ? \ + HeapDataFor (seam_queue, index) : \ + NULL) \ + + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * junk_worst_seam + * + * Delete the worst seam from the queue because it is full. + **********************************************************************/ +void junk_worst_seam(SEAM_QUEUE seams, SEAM *new_seam, float new_priority) { + SEAM *seam; + float priority; + + HeapPopWorst(seams, &priority, &seam); + if (priority > new_priority) { + delete_seam(seam); /*get rid of it */ + HeapPush (seams, new_priority, (char *) new_seam); + } + else { + delete_seam(new_seam); + HeapPush (seams, priority, (char *) seam); + } +} + + +/********************************************************************** + * choose_best_seam + * + * Choose the best seam that can be created by assembling this a + * collection of splits. A queue of all the possible seams is + * maintained. Each new split received is placed in that queue with + * its partial priority value. These values in the seam queue are + * evaluated and combined until a good enough seam is found. If no + * further good seams are being found then this function returns to the + * caller, who will send more splits. If this function is called with + * a split of NULL, then no further splits can be supplied by the + * caller. + **********************************************************************/ +void choose_best_seam(SEAM_QUEUE seam_queue, + SEAM_PILE *seam_pile, + SPLIT *split, + PRIORITY priority, + SEAM **seam_result, + TBLOB *blob) { + SEAM *seam; + TPOINT topleft; + TPOINT botright; + char str[80]; + float my_priority; + /* Add seam of split */ + my_priority = priority; + if (split != NULL) { + seam = new_seam (my_priority, + (split->point1->pos.x + split->point1->pos.x) / 2, + split, NULL, NULL); + if (chop_debug > 1) + print_seam ("Partial priority ", seam); + add_seam_to_queue (seam_queue, seam, (float) my_priority); + + if (my_priority > GOOD_PARTIAL_PRIORITY) + return; + } + + blob_bounding_box(blob, &topleft, &botright); + /* Queue loop */ + while (pop_next_seam (seam_queue, seam, my_priority)) { + /* Set full priority */ + my_priority = seam_priority (seam, topleft.x, botright.x); + if (chop_debug) { + sprintf (str, "Full my_priority %0.0f, ", my_priority); + print_seam(str, seam); + } + + if ((*seam_result == NULL || /* Replace answer */ + (*seam_result)->priority > my_priority) && my_priority < ok_split) { + /* No crossing */ + if (constrained_split (seam->split1, blob)) { + delete_seam(*seam_result); + clone_seam(*seam_result, seam); + (*seam_result)->priority = my_priority; + } + else { + delete_seam(seam); + seam = NULL; + my_priority = BAD_PRIORITY; + } + } + + if (my_priority < good_split) { + if (seam) + delete_seam(seam); + return; /* Made good answer */ + } + + if (seam) { + /* Combine with others */ + if (array_count (*seam_pile) < MAX_NUM_SEAMS + /*|| tessedit_truncate_chopper==0 */ ) { + combine_seam(seam_queue, *seam_pile, seam); + *seam_pile = array_push (*seam_pile, seam); + } + else + delete_seam(seam); + } + + my_priority = best_seam_priority (seam_queue); + if ((my_priority > ok_split) || + (my_priority > GOOD_PARTIAL_PRIORITY && split)) + return; + } +} + + +/********************************************************************** + * combine_seam + * + * Find other seams to combine with this one. The new seams that result + * from this union should be added to the seam queue. The return value + * tells whether or not any additional seams were added to the queue. + **********************************************************************/ +void combine_seam(SEAM_QUEUE seam_queue, SEAM_PILE seam_pile, SEAM *seam) { + register INT16 x; + register INT16 dist; + INT16 bottom1, top1; + INT16 bottom2, top2; + + SEAM *new_one; + SEAM *this_one; + + bottom1 = seam->split1->point1->pos.y; + if (seam->split1->point2->pos.y >= bottom1) + top1 = seam->split1->point2->pos.y; + else { + top1 = bottom1; + bottom1 = seam->split1->point2->pos.y; + } + if (seam->split2 != NULL) { + bottom2 = seam->split2->point1->pos.y; + if (seam->split2->point2->pos.y >= bottom2) + top2 = seam->split2->point2->pos.y; + else { + top2 = bottom2; + bottom2 = seam->split2->point2->pos.y; + } + } + else { + bottom2 = bottom1; + top2 = top1; + } + array_loop(seam_pile, x) { + this_one = (SEAM *) array_value (seam_pile, x); + dist = seam->location - this_one->location; + if (-SPLIT_CLOSENESS < dist && + dist < SPLIT_CLOSENESS && + seam->priority + this_one->priority < ok_split) { + if ( + /*!tessedit_fix_sideways_chops + || */ + (this_one->split1->point1->pos.y >= top1 + && this_one->split1->point2->pos.y >= top1 + || this_one->split1->point1->pos.y <= bottom1 + && this_one->split1->point2->pos.y <= bottom1) + && (this_one->split1->point1->pos.y >= top2 + && this_one->split1->point2->pos.y >= top2 + || this_one->split1->point1->pos.y <= bottom2 + && this_one->split1->point2->pos.y <= bottom2) + && (this_one->split2 == NULL + || (this_one->split2->point1->pos.y >= top1 + && this_one->split2->point2->pos.y >= top1 + || this_one->split2->point1->pos.y <= bottom1 + && this_one->split2->point2->pos.y <= bottom1) + && (this_one->split2->point1->pos.y >= top2 + && this_one->split2->point2->pos.y >= top2 + || this_one->split2->point1->pos.y <= bottom2 + && this_one->split2->point2->pos.y <= bottom2))) { + new_one = join_two_seams (seam, this_one); + if (chop_debug > 1) + print_seam ("Combo priority ", new_one); + add_seam_to_queue (seam_queue, new_one, new_one->priority); + } + } + } +} + + +/********************************************************************** + * constrained_split + * + * Constrain this split to obey certain rules. It must not cross any + * inner outline. It must not cut off a small chunk of the outline. + **********************************************************************/ +INT16 constrained_split(SPLIT *split, TBLOB *blob) { + TESSLINE *outline; + + if (is_little_chunk (split->point1, split->point2)) + return (FALSE); + + for (outline = blob->outlines; outline; outline = outline->next) { + if (split_bounds_overlap (split, outline) && + crosses_outline (split->point1, split->point2, outline->loop)) { + return (FALSE); + } + } + return (TRUE); +} + + +/********************************************************************** + * delete_seam_pile + * + * Delete the seams that are held in the seam pile. Destroy the splits + * that are referenced by these seams. + **********************************************************************/ +void delete_seam_pile(SEAM_PILE seam_pile) { + INT16 x; + + array_loop(seam_pile, x) { + delete_seam ((SEAM *) array_value (seam_pile, x)); + } + array_free(seam_pile); +} + + +/********************************************************************** + * pick_good_seam + * + * Find and return a good seam that will split this blob into two pieces. + * Work from the outlines provided. + **********************************************************************/ +SEAM *pick_good_seam(TBLOB *blob) { + SEAM_QUEUE seam_queue; + SEAM_PILE seam_pile; + POINT_GROUP point_heap; + PRIORITY priority; + EDGEPT *edge; + EDGEPT *points[MAX_NUM_POINTS]; + SEAM *seam = NULL; + TESSLINE *outline; + INT16 num_points = 0; + +#ifndef GRAPHICS_DISABLED + if (chop_debug) + display_splits = TRUE; + + draw_blob_edges(blob); +#endif + + point_heap = MakeHeap (MAX_NUM_POINTS); + for (outline = blob->outlines; outline; outline = outline->next) + prioritize_points(outline, point_heap); + + while (HeapPop (point_heap, &priority, &edge) == OK) { + if (num_points < MAX_NUM_POINTS) + points[num_points++] = (EDGEPT *) edge; + } + FreeHeap(point_heap); + + /* Initialize queue & pile */ + create_seam_pile(seam_pile); + create_seam_queue(seam_queue); + + try_point_pairs(points, num_points, seam_queue, &seam_pile, &seam, blob); + + try_vertical_splits(points, num_points, seam_queue, &seam_pile, &seam, blob); + + if (seam == NULL) { + choose_best_seam(seam_queue, &seam_pile, NULL, BAD_PRIORITY, &seam, blob); + } + else if (seam->priority > good_split) { + choose_best_seam (seam_queue, &seam_pile, NULL, seam->priority, + &seam, blob); + } + delete_seam_queue(seam_queue); + delete_seam_pile(seam_pile); + + if (seam) { + if (seam->priority > ok_split) { + delete_seam(seam); + seam = NULL; + } +#ifndef GRAPHICS_DISABLED + else if (display_splits) { + if (seam->split1) + mark_split (seam->split1); + if (seam->split2) + mark_split (seam->split2); + if (seam->split3) + mark_split (seam->split3); + if (chop_debug > 1) { + update_edge_window(); + edge_window_wait(); + } + } +#endif + } + + if (chop_debug) + display_splits = FALSE; + + return (seam); +} + + +/********************************************************************** + * seam_priority + * + * Assign a full priority value to the seam. + **********************************************************************/ +PRIORITY seam_priority(SEAM *seam, INT16 xmin, INT16 xmax) { + PRIORITY priority; + + if (seam->split1 == NULL) + priority = 0; + + else if (seam->split2 == NULL) { + priority = (seam->priority + + full_split_priority (seam->split1, xmin, xmax)); + } + + else if (seam->split3 == NULL) { + split_outline (seam->split2->point1, seam->split2->point2); + priority = (seam->priority + + full_split_priority (seam->split1, xmin, xmax)); + unsplit_outlines (seam->split2->point1, seam->split2->point2); + } + + else { + split_outline (seam->split2->point1, seam->split2->point2); + split_outline (seam->split3->point1, seam->split3->point2); + priority = (seam->priority + + full_split_priority (seam->split1, xmin, xmax)); + unsplit_outlines (seam->split3->point1, seam->split3->point2); + unsplit_outlines (seam->split2->point1, seam->split2->point2); + } + + return (priority); +} + + +/********************************************************************** + * try_point_pairs + * + * Try all the splits that are produced by pairing critical points + * together. See if any of them are suitable for use. Use a seam + * queue and seam pile that have already been initialized and used. + **********************************************************************/ +void +try_point_pairs (EDGEPT * points[MAX_NUM_POINTS], +INT16 num_points, +SEAM_QUEUE seam_queue, +SEAM_PILE * seam_pile, SEAM ** seam, TBLOB * blob) { + INT16 x; + INT16 y; + SPLIT *split; + PRIORITY priority; + + for (x = 0; x < num_points; x++) { + for (y = x + 1; y < num_points; y++) { + + if (points[y] && + weighted_edgept_dist (points[x], points[y], + x_y_weight) < split_length && + points[x] != points[y]->next && + points[y] != points[x]->next && + !is_exterior_point (points[x], points[y]) && + !is_exterior_point (points[y], points[x])) { + + split = new_split (points[x], points[y]); + priority = partial_split_priority (split); + + choose_best_seam(seam_queue, seam_pile, split, priority, seam, blob); + + if (*seam && (*seam)->priority < good_split) + return; + } + } + } + +} + + +/********************************************************************** + * try_vertical_splits + * + * Try all the splits that are produced by vertical projection to see + * if any of them are suitable for use. Use a seam queue and seam pile + * that have already been initialized and used. + **********************************************************************/ +void +try_vertical_splits (EDGEPT * points[MAX_NUM_POINTS], +INT16 num_points, +SEAM_QUEUE seam_queue, +SEAM_PILE * seam_pile, SEAM ** seam, TBLOB * blob) { + EDGEPT *vertical_point = NULL; + SPLIT *split; + INT16 x; + PRIORITY priority; + TESSLINE *outline; + + for (x = 0; x < num_points; x++) { + + if (*seam != NULL && (*seam)->priority < good_split) + return; + + vertical_point = NULL; + for (outline = blob->outlines; outline; outline = outline->next) { + vertical_projection_point (points[x], + outline->loop, &vertical_point); + } + + if (vertical_point && + points[x] != vertical_point->next && + vertical_point != points[x]->next && + weighted_edgept_dist (points[x], vertical_point, + x_y_weight) < split_length) { + + split = new_split (points[x], vertical_point); + priority = partial_split_priority (split); + + choose_best_seam(seam_queue, seam_pile, split, priority, seam, blob); + } + } +} diff --git a/wordrec/findseam.h b/wordrec/findseam.h new file mode 100644 index 0000000000..b3bb5a683c --- /dev/null +++ b/wordrec/findseam.h @@ -0,0 +1,69 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: findseam.h (Formerly findseam.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Thu May 16 17:05:17 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ + +#ifndef FINDSEAM_H +#define FINDSEAM_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "seam.h" +#include "oldheap.h" +#include "chop.h" + +typedef HEAP *SEAM_QUEUE; +typedef ARRAY SEAM_PILE; +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void junk_worst_seam(SEAM_QUEUE seams, SEAM *new_seam, float new_priority); + +void choose_best_seam(SEAM_QUEUE seam_queue, + SEAM_PILE *seam_pile, + SPLIT *split, + PRIORITY priority, + SEAM **seam_result, + TBLOB *blob); + +void combine_seam(SEAM_QUEUE seam_queue, SEAM_PILE seam_pile, SEAM *seam); + +INT16 constrained_split(SPLIT *split, TBLOB *blob); + +void delete_seam_pile(SEAM_PILE seam_pile); + +SEAM *pick_good_seam(TBLOB *blob); + +PRIORITY seam_priority(SEAM *seam, INT16 xmin, INT16 xmax); + +void try_point_pairs (EDGEPT * points[MAX_NUM_POINTS], +INT16 num_points, +SEAM_QUEUE seam_queue, +SEAM_PILE * seam_pile, SEAM ** seam, TBLOB * blob); + +void try_vertical_splits (EDGEPT * points[MAX_NUM_POINTS], +INT16 num_points, +SEAM_QUEUE seam_queue, +SEAM_PILE * seam_pile, SEAM ** seam, TBLOB * blob); +#endif diff --git a/wordrec/gradechop.cpp b/wordrec/gradechop.cpp new file mode 100644 index 0000000000..68a14dee29 --- /dev/null +++ b/wordrec/gradechop.cpp @@ -0,0 +1,226 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: gradechop.c (Formerly gradechop.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 30 16:06:27 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "gradechop.h" +#include "debug.h" +#include "olutil.h" +#include "chop.h" +#include + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define CENTER_GRADE_CAP 25.0 + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * find_bounds_loop + * + * This is a macro to be used by set_outline_bounds. + **********************************************************************/ + +#define find_bounds_loop(point1,point2,x_min,x_max) \ + x_min = point2->pos.x; \ + x_max = point2->pos.x; \ + \ + this_point = point1; \ + do { \ + x_min = min (this_point->pos.x, x_min); \ + x_max = max (this_point->pos.x, x_max); \ + this_point = this_point->next; \ + } \ + while (this_point != point2 && this_point != point1) \ + + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * full_split_priority + * + * Assign a priority to this split based on the features that it has. + * Part of the priority has already been calculated so just return the + * additional amount for the bounding box type information. + **********************************************************************/ +PRIORITY full_split_priority(SPLIT *split, INT16 xmin, INT16 xmax) { + BOUNDS_RECT rect; + + set_outline_bounds (split->point1, split->point2, rect); + + if (xmin < min (rect[0], rect[2]) && xmax > max (rect[1], rect[3])) + return (999.0); + + return (grade_overlap (rect) + + grade_center_of_blob (rect) + grade_width_change (rect)); +} + + +/********************************************************************** + * grade_center_of_blob + * + * Return a grade for the a split. Rank it on closeness to the center + * of the original blob + * 0 = "perfect" + * 100 = "no way jay" + **********************************************************************/ +PRIORITY grade_center_of_blob(register BOUNDS_RECT rect) { + register PRIORITY grade; + + grade = (rect[1] - rect[0]) - (rect[3] - rect[2]); + if (grade < 0) + grade = -grade; + + grade *= center_knob; + grade = min (CENTER_GRADE_CAP, grade); + return (max (0.0, grade)); +} + + +/********************************************************************** + * grade_overlap + * + * Return a grade for this split for the overlap of the resultant blobs. + * 0 = "perfect" + * 100 = "no way jay" + **********************************************************************/ +PRIORITY grade_overlap(register BOUNDS_RECT rect) { + register PRIORITY grade; + register INT16 width1; + register INT16 width2; + register INT16 overlap; + + width1 = rect[3] - rect[2]; + width2 = rect[1] - rect[0]; + + overlap = min (rect[1], rect[3]) - max (rect[0], rect[2]); + width1 = min (width1, width2); + if (overlap == width1) + return (100.0); /* Total overlap */ + + width1 = 2 * overlap - width1; /* Extra penalty for too */ + overlap += max (0, width1); /* much overlap */ + + grade = overlap * overlap_knob; + + return (max (0.0, grade)); +} + + +/********************************************************************** + * grade_split_length + * + * Return a grade for the length of this split. + * 0 = "perfect" + * 100 = "no way jay" + **********************************************************************/ +PRIORITY grade_split_length(register SPLIT *split) { + register PRIORITY grade; + register float split_length; + + split_length = weighted_edgept_dist (split->point1, split->point2, + x_y_weight); + + if (split_length <= 0) + grade = 0; + else + grade = sqrt (split_length) * split_dist_knob; + + return (max (0.0, grade)); +} + + +/********************************************************************** + * grade_sharpness + * + * Return a grade for the sharpness of this split. + * 0 = "perfect" + * 100 = "no way jay" + **********************************************************************/ +PRIORITY grade_sharpness(register SPLIT *split) { + register PRIORITY grade; + + grade = point_priority (split->point1) + point_priority (split->point2); + + if (grade < -360.0) + grade = 0; + else + grade += 360.0; + + grade *= sharpness_knob; /* Values 0 to -360 */ + + return (grade); +} + + +/********************************************************************** + * grade_width_change + * + * Return a grade for the change in width of the resultant blobs. + * 0 = "perfect" + * 100 = "no way jay" + **********************************************************************/ +PRIORITY grade_width_change(register BOUNDS_RECT rect) { + register PRIORITY grade; + register INT32 width1; + register INT32 width2; + + width1 = rect[3] - rect[2]; + width2 = rect[1] - rect[0]; + + grade = 20 - (max (rect[1], rect[3]) + - min (rect[0], rect[2]) - max (width1, width2)); + + grade *= width_change_knob; + + return (max (0.0, grade)); +} + + +/********************************************************************** + * set_outline_bounds + * + * Set up the limits for the x coordinate of the outline. + **********************************************************************/ +void set_outline_bounds(register EDGEPT *point1, + register EDGEPT *point2, + BOUNDS_RECT rect) { + register EDGEPT *this_point; + register INT16 x_min; + register INT16 x_max; + + find_bounds_loop(point1, point2, x_min, x_max); + + rect[0] = x_min; + rect[1] = x_max; + + find_bounds_loop(point2, point1, x_min, x_max); + + rect[2] = x_min; + rect[3] = x_max; +} diff --git a/wordrec/gradechop.h b/wordrec/gradechop.h new file mode 100644 index 0000000000..a8389440d9 --- /dev/null +++ b/wordrec/gradechop.h @@ -0,0 +1,91 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: gradechop.h (Formerly gradechop.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 9 16:40:39 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ + +#ifndef GRADECHOP_H +#define GRADECHOP_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "seam.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef INT16 BOUNDS_RECT[4]; + +/*---------------------------------------------------------------------- + V a r i a b l e s +---------------------------------------------------------------------*/ +extern int x_y_weight; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * partial_split_priority + * + * Assign a priority to this split based on the features that it has. + * Grade it according to the different rating schemes and return the + * value of its goodness. + **********************************************************************/ + +#define partial_split_priority(split) \ +(grade_split_length (split) + \ + grade_sharpness (split)) \ + + +/********************************************************************** + * split_bounds_overlap + * + * Check to see if this split might overlap with this outline. Return + * TRUE if there is a positive overlap in the bounding boxes of the two. + **********************************************************************/ + +#define split_bounds_overlap(split,outline) \ +(outline->topleft.x <= max (split->point1->pos.x,split->point2->pos.x) && \ + outline->botright.x >= min (split->point1->pos.x,split->point2->pos.x) && \ + outline->botright.y <= max (split->point1->pos.y,split->point2->pos.y) && \ + outline->topleft.y >= min (split->point1->pos.y,split->point2->pos.y)) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +PRIORITY full_split_priority(SPLIT *split, INT16 xmin, INT16 xmax); + +PRIORITY grade_center_of_blob(register BOUNDS_RECT rect); + +PRIORITY grade_overlap(register BOUNDS_RECT rect); + +PRIORITY grade_split_length(register SPLIT *split); + +PRIORITY grade_sharpness(register SPLIT *split); + +PRIORITY grade_width_change(register BOUNDS_RECT rect); + +void set_outline_bounds(register EDGEPT *point1, + register EDGEPT *point2, + BOUNDS_RECT rect); +#endif diff --git a/wordrec/heuristic.cpp b/wordrec/heuristic.cpp new file mode 100644 index 0000000000..5c74826404 --- /dev/null +++ b/wordrec/heuristic.cpp @@ -0,0 +1,194 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: heuristic.c (Formerly heuristic.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Wed Jul 10 14:15:08 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "heuristic.h" +#include "baseline.h" +#include "metrics.h" +#include "freelist.h" +#include + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +#define MAX_SQUAT 2.0 /* Width ratio */ +#define BAD_RATING 1000.0 /* No valid blob */ + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * prioritize_state + * + * Create a priority for this state. It represents the urgency of + * checking this state. + **********************************************************************/ +FLOAT32 prioritize_state(CHUNKS_RECORD *chunks_record, + SEARCH_RECORD *the_search, + STATE *old_state) { + FLOAT32 width_pri; + FLOAT32 match_pri; + + match_pri = rating_priority (chunks_record, the_search->this_state, + old_state, the_search->num_joints); + + width_pri = width_priority (chunks_record, the_search->this_state, + the_search->num_joints) * 1000.0; + + record_priorities(the_search, old_state, match_pri, width_pri); + + return (width_pri + match_pri); +} + + +/********************************************************************** + * rating_priority + * + * Assign a segmentation priority based on the ratings of the blobs + * (in that segmentation) that have been classified. The average + * "goodness" (i.e. rating / weight) for each blob is used to indicate + * the segmentation priority. + **********************************************************************/ +FLOAT32 rating_priority(CHUNKS_RECORD *chunks_record, + STATE *state, + STATE *old_state, + int num_joints) { + PIECES_STATE blob_chunks; + INT16 x; + INT16 y; + CHOICES this_choice; + INT16 first_chunk = 0; + INT16 last_chunk; + INT16 ratings = 0; + INT16 weights = 0; + + bin_to_pieces(state, num_joints, blob_chunks); + + for (x = 0; blob_chunks[x]; x++) { + // Iterate each blob + last_chunk = first_chunk + blob_chunks[x] - 1; + + this_choice = matrix_get (chunks_record->ratings, + first_chunk, last_chunk); + + if (this_choice == NIL) + return (BAD_RATING); + + if (this_choice != NOT_CLASSIFIED) { + + ratings += (INT16) best_probability (this_choice); + for (y = first_chunk; y <= last_chunk; y++) { + weights += (INT16) (chunks_record->weights[y]); + } + } + first_chunk += blob_chunks[x]; + } + if (weights <= 0) + weights = 1; + return ((FLOAT32) ratings / weights); +} + + +/********************************************************************** + * state_char_widths + * + * Return a character width record corresponding to the character + * width that will be generated in this segmentation state. + **********************************************************************/ +WIDTH_RECORD *state_char_widths(WIDTH_RECORD *chunk_widths, + STATE *state, + int num_joints, + SEARCH_STATE *search_state) { + WIDTH_RECORD *width_record; + int num_blobs; + int x; + int y; + int i; + SEARCH_STATE new_chunks; + + new_chunks = bin_to_chunks (state, num_joints); + + num_blobs = new_chunks[0] + 1; + width_record = (WIDTH_RECORD *) memalloc (sizeof (int) * num_blobs * 2); + width_record->num_chars = num_blobs; + + x = 0; + for (i = 1; i <= new_chunks[0] + 1; i++) { + if (i > new_chunks[0]) + y = num_joints; + else + y = x + new_chunks[i]; + + width_record->widths[2 * i - 2] = chunks_width (chunk_widths, x, y); + + if (i <= new_chunks[0]) + width_record->widths[2 * i - 1] = chunks_gap (chunk_widths, y); + + x = y + 1; + } + + *search_state = new_chunks; + return (width_record); +} + + +/********************************************************************** + * width_priority + * + * Return a priority value for this word segmentation based on the + * character widths present in the new segmentation. + **********************************************************************/ +FLOAT32 width_priority(CHUNKS_RECORD *chunks_record, + STATE *state, + int num_joints) { + SEARCH_STATE new_chunks; + FLOAT32 result = 0.0; + WIDTH_RECORD *width_record; + FLOAT32 squat; + int x; + + width_record = state_char_widths (chunks_record->chunk_widths, + state, num_joints, &new_chunks); + for (x = 0; x < width_record->num_chars; x++) { + + squat = width_record->widths[2 * x]; + if (!baseline_enable) { + squat /= chunks_record->row->lineheight; + } + else { + squat /= BASELINE_SCALE; + } + + if (squat > MAX_SQUAT) + result += squat - MAX_SQUAT; + + } + + memfree(new_chunks); + free_widths(width_record); + + return (result); +} diff --git a/wordrec/heuristic.h b/wordrec/heuristic.h new file mode 100644 index 0000000000..f9bfc80c45 --- /dev/null +++ b/wordrec/heuristic.h @@ -0,0 +1,120 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: heuristic.h (Formerly heuristic.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 9 17:14:44 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef HEURISTIC_H +#define HEURISTIC_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "associate.h" +#include "bestfirst.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +FLOAT32 prioritize_state(CHUNKS_RECORD *chunks_record, + SEARCH_RECORD *the_search, + STATE *old_state); + +FLOAT32 rating_priority(CHUNKS_RECORD *chunks_record, + STATE *state, + STATE *old_state, + int num_joints); + +WIDTH_RECORD *state_char_widths(WIDTH_RECORD *chunk_widths, + STATE *state, + int num_joints, + SEARCH_STATE *search_state); + +FLOAT32 width_priority(CHUNKS_RECORD *chunks_record, + STATE *state, + int num_joints); + +/* +#if defined(__STDC__) || defined(__cplusplus) || MAC_OR_DOS +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* heuristic.c +PROBABILITY best_char_rating + _ARGS((CHUNKS_RECORD *chunks_record, + int first_chunk, + int last_chunk, + char *word)); + +STATE *first_segmentation + _ARGS((CHUNKS_RECORD *chunks_record)); + +FLOAT32 gap_priority + _ARGS((CHUNKS_RECORD *chunks_record, + STATE *state, + int num_joints)); + +FLOAT32 match_priority + _ARGS((CHUNKS_RECORD *chunks_record, + STATE *state, + STATE *old_state, + int num_joints)); + +FLOAT32 frequency_priority + _ARGS((STATE *state, + STATE *old_state, + int num_joints)); + +STATE *pick_good_segmentation + _ARGS((CHUNKS_RECORD *chunks_record)); + +void print_widths + _ARGS((FILE *file, + char *string, + WIDTH_RECORD *width_array)); + +FLOAT32 prioritize_state + _ARGS((CHUNKS_RECORD *chunks_record, + SEARCH_RECORD *the_search, + STATE *old_state)); + +FLOAT32 rating_priority + _ARGS((CHUNKS_RECORD *chunks_record, + STATE *state, + STATE *old_state, + int num_joints)); + +WIDTH_RECORD *state_char_widths + _ARGS((WIDTH_RECORD *chunk_widths, + STATE *state, + int num_joints, + SEARCH_STATE *search_state)); + +FLOAT32 width_priority + _ARGS((CHUNKS_RECORD *chunks_record, + STATE *state, + int num_joints)); + +#undef _ARGS +*/ +#endif diff --git a/wordrec/makechop.cpp b/wordrec/makechop.cpp new file mode 100644 index 0000000000..156adab78b --- /dev/null +++ b/wordrec/makechop.cpp @@ -0,0 +1,281 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: makechop.c (Formerly makechop.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon Jul 29 15:50:42 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "makechop.h" +#include "render.h" +#include "structures.h" +#ifdef __UNIX__ +#include +#include +#endif + +/*---------------------------------------------------------------------- + Public Function Code +----------------------------------------------------------------------*/ +/********************************************************************** + * apply_seam + * + * Split this blob into two blobs by applying the splits included in + * the seam description. + **********************************************************************/ +void apply_seam(TBLOB *blob, TBLOB *other_blob, SEAM *seam) { + check_outline_mem(); + if (seam->split1 == NULL) { + divide_blobs (blob, other_blob, seam->location); + } + else if (seam->split2 == NULL) { + make_split_blobs(blob, other_blob, seam); + } + else if (seam->split3 == NULL) { + make_double_split(blob, other_blob, seam); + } + else { + make_triple_split(blob, other_blob, seam); + } + + check_outline_mem(); +} + + +/********************************************************************** + * divide_blobs + * + * Create two blobs by grouping the outlines in the appropriate blob. + * The outlines that are beyond the location point are moved to the + * other blob. The ones whose x location is less than that point are + * retained in the original blob. + **********************************************************************/ +void divide_blobs(TBLOB *blob, TBLOB *other_blob, INT32 location) { + TESSLINE *outline; + TESSLINE *outline1 = NULL; + TESSLINE *outline2 = NULL; + + outline = blob->outlines; + blob->outlines = NULL; + + while (outline != NULL) { + if ((outline->topleft.x + outline->botright.x) / 2 < location) { + /* Outline is in 1st blob */ + if (outline1) { + outline1->next = outline; + } + else { + blob->outlines = outline; + } + outline1 = outline; + } + else { + /* Outline is in 2nd blob */ + if (outline2) { + outline2->next = outline; + } + else { + other_blob->outlines = outline; + } + outline2 = outline; + } + + outline = outline->next; + } + + if (outline1) + outline1->next = NULL; + if (outline2) + outline2->next = NULL; +} + + +/********************************************************************** + * form_two_blobs + * + * Group the outlines from the first blob into both of them. Do so + * according to the information about the split. + **********************************************************************/ +void form_two_blobs(TBLOB *blob, TBLOB *other_blob, INT32 location) { + setup_blob_outlines(blob); + + divide_blobs(blob, other_blob, location); + + eliminate_duplicate_outlines(blob); + eliminate_duplicate_outlines(other_blob); + + correct_blob_order(blob, other_blob); + +#ifndef GRAPHICS_DISABLED + if (chop_debug) { + display_blob(blob, Red); + #ifdef __UNIX__ + sleep (1); + #endif + display_blob(other_blob, Cyan); + } +#endif +} + + +/********************************************************************** + * make_double_split + * + * Create two blobs out of one by splitting the original one in half. + * Return the resultant blobs for classification. + **********************************************************************/ +void make_double_split(TBLOB *blob, TBLOB *other_blob, SEAM *seam) { + make_single_split (blob->outlines, seam->split1); + make_single_split (blob->outlines, seam->split2); + form_two_blobs (blob, other_blob, seam->location); +} + + +/********************************************************************** + * make_single_split + * + * Create two outlines out of one by splitting the original one in half. + * Return the resultant outlines. + **********************************************************************/ +void make_single_split(TESSLINE *outlines, SPLIT *split) { + assert (outlines != NULL); + + split_outline (split->point1, split->point2); + + while (outlines->next != NULL) + outlines = outlines->next; + + outlines->next = newoutline (); + outlines->next->loop = split->point1; + outlines->next->child = NULL; + setup_outline (outlines->next); + + outlines = outlines->next; + + outlines->next = newoutline (); + outlines->next->loop = split->point2; + outlines->next->child = NULL; + setup_outline (outlines->next); + + outlines->next->next = NULL; +} + + +/********************************************************************** + * make_split_blobs + * + * Create two blobs out of one by splitting the original one in half. + * Return the resultant blobs for classification. + **********************************************************************/ +void make_split_blobs(TBLOB *blob, TBLOB *other_blob, SEAM *seam) { + make_single_split (blob->outlines, seam->split1); + + form_two_blobs (blob, other_blob, seam->location); +} + + +/********************************************************************** + * make_triple_split + * + * Create two blobs out of one by splitting the original one in half. + * This splitting is accomplished by applying three separate splits on + * the outlines. Three of the starting outlines will produce two ending + * outlines. Return the resultant blobs for classification. + **********************************************************************/ +void make_triple_split(TBLOB *blob, TBLOB *other_blob, SEAM *seam) { + make_single_split (blob->outlines, seam->split1); + make_single_split (blob->outlines, seam->split2); + make_single_split (blob->outlines, seam->split3); + + form_two_blobs (blob, other_blob, seam->location); +} + + +/********************************************************************** + * undo_seam + * + * Remove the seam between these two blobs. Produce one blob as a + * result. The seam may consist of one, two, or three splits. Each + * of these split must be removed from the outlines. + **********************************************************************/ +void undo_seam(TBLOB *blob, TBLOB *other_blob, SEAM *seam) { + TESSLINE *outline; + + if (!seam) + return; /* Append other blob outlines */ + if (blob->outlines == NULL) { + blob->outlines = other_blob->outlines; + other_blob->outlines = NULL; + } + + outline = blob->outlines; + while (outline->next) + outline = outline->next; + outline->next = other_blob->outlines; + oldblob(other_blob); + + if (seam->split1 == NULL) { + } + else if (seam->split2 == NULL) { + undo_single_split (blob, seam->split1); + } + else if (seam->split3 == NULL) { + undo_single_split (blob, seam->split1); + undo_single_split (blob, seam->split2); + } + else { + undo_single_split (blob, seam->split3); + undo_single_split (blob, seam->split2); + undo_single_split (blob, seam->split1); + } + + setup_blob_outlines(blob); + eliminate_duplicate_outlines(blob); + + check_outline_mem(); +} + + +/********************************************************************** + * undo_single_split + * + * Undo a seam that is made by a single split. Perform the correct + * magic to reconstruct the appropriate set of outline data structures. + **********************************************************************/ +void undo_single_split(TBLOB *blob, SPLIT *split) { + TESSLINE *outline1; + TESSLINE *outline2; + /* Modify edge points */ + unsplit_outlines (split->point1, split->point2); + + outline1 = newoutline (); + outline1->next = blob->outlines; + blob->outlines = outline1; + outline1->loop = split->point1; + outline1->child = NULL; + + outline2 = newoutline (); + outline2->next = blob->outlines; + blob->outlines = outline2; + outline2->loop = split->point2; + outline2->child = NULL; +} diff --git a/wordrec/makechop.h b/wordrec/makechop.h new file mode 100644 index 0000000000..5d674af67a --- /dev/null +++ b/wordrec/makechop.h @@ -0,0 +1,69 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: makechop.h (Formerly makechop.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon Jul 29 13:33:23 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef MAKECHOP_H +#define MAKECHOP_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "chop.h" +#include "olutil.h" + +/*---------------------------------------------------------------------- + M a c r o s +---------------------------------------------------------------------*/ +/********************************************************************** + * is_split_outline + * + * Check to see if both sides of the split fall within the bounding + * box of this outline. + **********************************************************************/ + +#define is_split_outline(outline,split) \ +(point_in_outline (split->point1, outline) && \ + point_in_outline (split->point2, outline)) \ + + +/*---------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------*/ +void apply_seam(TBLOB *blob, TBLOB *other_blob, SEAM *seam); + +void divide_blobs(TBLOB *blob, TBLOB *other_blob, INT32 location); + +void form_two_blobs(TBLOB *blob, TBLOB *other_blob, INT32 location); + +void make_double_split(TBLOB *blob, TBLOB *other_blob, SEAM *seam); + +void make_single_split(TESSLINE *outlines, SPLIT *split); + +void make_split_blobs(TBLOB *blob, TBLOB *other_blob, SEAM *seam); + +void make_triple_split(TBLOB *blob, TBLOB *other_blob, SEAM *seam); + +void undo_seam(TBLOB *blob, TBLOB *other_blob, SEAM *seam); + +void undo_single_split(TBLOB *blob, SPLIT *split); +#endif diff --git a/wordrec/matchtab.cpp b/wordrec/matchtab.cpp new file mode 100644 index 0000000000..4000c1eb63 --- /dev/null +++ b/wordrec/matchtab.cpp @@ -0,0 +1,191 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: matchtab.c (Formerly matchtab.c) + * Description: Match table to retain blobs that were matched. + * Author: Mark Seaman, OCR Technology + * Created: Mon Jan 29 09:00:56 1990 + * Modified: Tue Mar 19 15:09:06 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "matchtab.h" +#include "freelist.h" +#include "callcpp.h" +#include "blobs.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef struct _MATCH_ +{ + int topleft; + int botright; + LIST rating; +} MATCH; + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +MATCH *match_table; +//?int missed_count = 0; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +#define NUM_MATCH_ENTRIES 500 /* Entries in match_table */ + +/********************************************************************** + * blank_entry + * + * Test an element in the blob match table to see if it is blank. + * Return a non-zero value if it is blank. + **********************************************************************/ + +#define blank_entry(match_table,x) \ +(! (match_table[x].topleft | match_table[x].botright)) + +/*---------------------------------------------------------------------- + Public Function Code +----------------------------------------------------------------------*/ +/********************************************************************** + * init_match_table + * + * Create and clear a match table to be used to speed up the splitter. + **********************************************************************/ +static int been_initialized = 0; +void init_match_table() { + int x; + + if (been_initialized) { + /* Reclaim old choices */ + for (x = 0; x < NUM_MATCH_ENTRIES; x++) { + if ((!blank_entry (match_table, x)) && match_table[x].rating) + destroy_nodes (match_table[x].rating, free_choice); + } + } + else { + /* Allocate memory once */ + been_initialized = 1; + match_table = (MATCH *) memalloc (sizeof (MATCH) * NUM_MATCH_ENTRIES); + } + /* Initialize the table */ + for (x = 0; x < NUM_MATCH_ENTRIES; x++) { + match_table[x].topleft = 0; + match_table[x].botright = 0; + match_table[x].rating = NULL; + } +} + +void end_match_table() { + if (been_initialized) { + init_match_table(); + memfree(match_table); + match_table = NULL; + been_initialized = 0; + } +} + + +/********************************************************************** + * put_match + * + * Put a new blob and its corresponding match ratings into the match + * table. + **********************************************************************/ +void put_match(TBLOB *blob, CHOICES ratings) { + unsigned int topleft; + unsigned int botright; + unsigned int start; + TPOINT tp_topleft; + TPOINT tp_botright; + int x; + /* Hash into table */ + blob_bounding_box(blob, &tp_topleft, &tp_botright); + topleft = *(unsigned int *) &tp_topleft; + botright = *(unsigned int *) &tp_botright; + start = (topleft * botright) % NUM_MATCH_ENTRIES; + + /* Look for empty */ + x = start; + do { + if (blank_entry (match_table, x)) { + /* Add this entry */ + match_table[x].topleft = topleft; + match_table[x].botright = botright; + match_table[x].rating = copy_choices (ratings); + return; + } + if (++x >= NUM_MATCH_ENTRIES) + x = 0; + } + while (x != start); + + cprintf ("error: Match table is full\n"); +} + + +/********************************************************************** + * get_match + * + * Look up this blob in the match table to see if it needs to be + * matched. If it is not present then NULL is returned. + **********************************************************************/ +CHOICES get_match(TBLOB *blob) { + unsigned int topleft; + unsigned int botright; + TPOINT tp_topleft; + TPOINT tp_botright; + /* Do starting hash */ + blob_bounding_box(blob, &tp_topleft, &tp_botright); + topleft = *(unsigned int *) &tp_topleft; + botright = *(unsigned int *) &tp_botright; + return (get_match_by_bounds (topleft, botright)); +} + + +/********************************************************************** + * get_match_by_bounds + * + * Look up this blob in the match table to see if it needs to be + * matched. If it is not present then NULL is returned. + **********************************************************************/ +CHOICES get_match_by_bounds(unsigned int topleft, unsigned int botright) { + unsigned int start; + int x; + /* Do starting hash */ + start = (topleft * botright) % NUM_MATCH_ENTRIES; + /* Search for match */ + x = start; + do { + /* Not found when blank */ + if (blank_entry (match_table, x)) + break; + /* Is this the match ? */ + if (match_table[x].topleft == topleft && + match_table[x].botright == botright) { + return (copy_choices (match_table[x].rating)); + } + if (++x >= NUM_MATCH_ENTRIES) + x = 0; + } + while (x != start); + + return (NIL); +} diff --git a/wordrec/matchtab.h b/wordrec/matchtab.h new file mode 100644 index 0000000000..2ab77cf193 --- /dev/null +++ b/wordrec/matchtab.h @@ -0,0 +1,45 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: matchtab.h (Formerly matchtab.h) + * Description: Match table to retain blobs that were matched. + * Author: Mark Seaman, OCR Technology + * Created: Mon Jan 29 09:00:56 1990 + * Modified: Tue Mar 19 15:38:19 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef MATCHTAB_H +#define MATCHTAB_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "choices.h" +#include "tessclas.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void init_match_table(); +void end_match_table(); + +void put_match(TBLOB *blob, CHOICES ratings); + +CHOICES get_match(TBLOB *blob); + +CHOICES get_match_by_bounds(unsigned int topleft, unsigned int botright); +#endif diff --git a/wordrec/matrix.cpp b/wordrec/matrix.cpp new file mode 100644 index 0000000000..87c92b26c0 --- /dev/null +++ b/wordrec/matrix.cpp @@ -0,0 +1,118 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: matrix.c (Formerly matrix.c) + * Description: Ratings matrix code. (Used by associator) + * Author: Mark Seaman, OCR Technology + * Created: Wed May 16 13:18:47 1990 + * Modified: Wed Mar 20 09:44:47 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "matrix.h" +#include "cutil.h" +#include "freelist.h" +#include "callcpp.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * create_matrix + * + * Allocate a piece of memory to hold a matrix of choice list pointers. + * initialize all the elements of the matrix to NULL. + **********************************************************************/ +MATRIX create_matrix(int dimension) { + MATRIX m; + int x; + int y; + + m = (MATRIX) memalloc ((dimension * dimension + 1) * sizeof (LIST)); + m[0] = (LIST) dimension; + for (x = 0; x < dimension; x++) + for (y = 0; y < dimension; y++) + matrix_put(m, x, y, NOT_CLASSIFIED); + return (m); +} + + +/********************************************************************** + * free_matrix + * + * Deallocate the memory taken up by a matrix of match ratings. + *********************************************************************/ +void free_matrix(MATRIX matrix) { + int x; + int y; + int dimension = matrix_dimension (matrix); + CHOICES matrix_cell; + + for (x = 0; x < dimension; x++) { + for (y = 0; y < dimension; y++) { + matrix_cell = matrix_get (matrix, x, y); + if (matrix_cell != NOT_CLASSIFIED) + free_choices(matrix_cell); + } + } + memfree(matrix); +} + + +/********************************************************************** + * print_matrix + * + * Print the best guesses out of the match rating matrix. + **********************************************************************/ +void print_matrix(MATRIX rating_matrix) { + int x; + int dimension; + int spread; + CHOICES rating; + + cprintf ("Ratings Matrix (top choices)\n"); + + dimension = matrix_dimension (rating_matrix); + /* Do each diagonal */ + for (spread = 0; spread < dimension; spread++) { + /* For each spot */ + for (x = 0; x < dimension - spread; x++) { + /* Process one square */ + rating = matrix_get (rating_matrix, x, x + spread); + + if (rating != NOT_CLASSIFIED) { + cprintf ("\t[%d,%d] : ", x, x + spread); + if (first (rating)) + cprintf ("%-10s%4.0f\t|\t", + class_string (first (rating)), + class_probability (first (rating))); + if (second (rating)) + cprintf ("%-10s%4.0f\t|\t", + class_string (second (rating)), + class_probability (second (rating))); + if (third (rating)) + cprintf ("%-10s%4.0f\n", + class_string (third (rating)), + class_probability (third (rating))); + else + new_line(); + } + } + } +} diff --git a/wordrec/matrix.h b/wordrec/matrix.h new file mode 100644 index 0000000000..4d0200c5e3 --- /dev/null +++ b/wordrec/matrix.h @@ -0,0 +1,104 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: matrix.h (Formerly matrix.h) + * Description: Ratings matrix code. (Used by associator) + * Author: Mark Seaman, OCR Technology + * Created: Wed May 16 13:22:06 1990 + * Modified: Tue Mar 19 16:00:20 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef MATRIX_H +#define MATRIX_H + +#include "oldlist.h" +#include "choices.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef LIST *MATRIX; /* Matrix of LIST */ +#define NOT_CLASSIFIED (CHOICES) -1 + +/*---------------------------------------------------------------------- + Macros +----------------------------------------------------------------------*/ +/********************************************************************** + * matrix_dimension + * + * Provide the dimension of this square matrix. + **********************************************************************/ + +#define matrix_dimension(matrix) \ +((long) matrix [0]) + +/********************************************************************** + * matrix_index + * + * Expression to select a specific location in the matrix. + **********************************************************************/ + +#define matrix_index(matrix,column,row) \ +((row) * matrix_dimension(matrix) + (column) + 1) + +/********************************************************************** + * matrix_put + * + * Put a list element into the matrix at a specific location. + **********************************************************************/ + +#define matrix_put(matrix,column,row,thing) \ +((matrix) [matrix_index ((matrix), (column), (row))] = (thing)) + +/********************************************************************** + * matrix_get + * + * Get the item at a specified location from the matrix. + **********************************************************************/ + +#define matrix_get(matrix,column,row) \ +((matrix) [matrix_index ((matrix), (column), (row))]) + +/*--------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------*/ +MATRIX create_matrix(int dimension); + +void free_matrix(MATRIX matrix); + +void print_matrix(MATRIX rating_matrix); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* matrix.c +MATRIX create_matrix + _ARGS((int dimension)); + +MATRIX free_matrix + _ARGS((MATRIX matrix)); + +void print_matrix + _ARGS((MATRIX rating_matrix)); + +#undef _ARGS +*/ +#endif diff --git a/wordrec/measure.h b/wordrec/measure.h new file mode 100644 index 0000000000..978932ae00 --- /dev/null +++ b/wordrec/measure.h @@ -0,0 +1,135 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: measure.h (Formerly measure.h) + * Description: Statistics for a group of single measurements + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon Apr 8 09:42:28 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + */ + +#ifndef MEASURE_H +#define MEASURE_H + +/* +---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------- +*/ + +#include + +/* +---------------------------------------------------------------------- + T y p e s +---------------------------------------------------------------------- +*/ + +typedef struct +{ + long num_samples; + float sum_of_samples; + float sum_of_squares; +} MEASUREMENT; + +/* +---------------------------------------------------------------------- + M a c r o s +---------------------------------------------------------------------- +*/ + +/********************************************************************** + * add_sample + * + * Add one more sample to a measurement. + **********************************************************************/ + +#define ADD_SAMPLE(m,s) \ +(m.sum_of_samples += (float) (s), \ + m.sum_of_squares += (float) (s) * (float) (s), \ + ++m.num_samples) + +/********************************************************************** + * mean + * + * Return the mean value of the measurement. + **********************************************************************/ + +#define MEAN(m) \ +((m).num_samples ? \ + ((float) ((m).sum_of_samples / (m).num_samples)) : \ + 0) + +/********************************************************************** + * new_measurement + * + * Initalize a record to hold a measurement of a group of individual + * samples. + **********************************************************************/ + +#define new_measurement(m) \ +((m).num_samples = 0, \ + (m).sum_of_samples = 0, \ + (m).sum_of_squares = 0) + +/********************************************************************** + * number_of_samples + * + * Return the number of samples in a measurement. + **********************************************************************/ + +#define number_of_samples(m) \ +((m).num_samples) + +/********************************************************************** + * standard_deviation + * + * Return the standard deviation of the measurement. + **********************************************************************/ + +#define standard_deviation(m) \ +((float) sqrt (VARIANCE (m))) + +/********************************************************************** + * variance + * + * Return the variance of the measurement. + **********************************************************************/ + +#define VARIANCE(m) \ +(((m).num_samples > 1) ? \ + ((float) \ + (((m).num_samples * (m).sum_of_squares - \ + (m).sum_of_samples * (m).sum_of_samples) / \ + (((m).num_samples - 1) * (m).num_samples))) : \ + 0) + +/********************************************************************** + * print_summary + * + * Summarize a MEASUREMENT record. + **********************************************************************/ + +#define print_summary(string,measure) \ +cprintf ("\t%-20s \tn = %d, \tm = %4.2f, \ts = %4.2f\n ", \ + string, \ + number_of_samples (measure), \ + MEAN (measure), \ + standard_deviation (measure)) +#endif diff --git a/wordrec/metrics.cpp b/wordrec/metrics.cpp new file mode 100644 index 0000000000..bd15299de5 --- /dev/null +++ b/wordrec/metrics.cpp @@ -0,0 +1,358 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: metrics.c (Formerly metrics.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 30 17:02:07 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "metrics.h" +#include "bestfirst.h" +#include "associate.h" +#include "tally.h" +#include "plotseg.h" +#include "globals.h" +#include "wordclass.h" +#include "intmatcher.h" +#include "freelist.h" +#include "djmenus.h" +#include "callcpp.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static int states_timed_out1; /* Counters */ +static int states_timed_out2; +static int words_segmented1; +static int words_segmented2; +static int segmentation_states1; +static int segmentation_states2; +static int save_priorities; + +int words_chopped1; +int words_chopped2; +int chops_attempted1; +int chops_performed1; +int chops_attempted2; +int chops_performed2; + +int character_count; +int word_count; +int chars_classified; + +MEASUREMENT num_pieces; +MEASUREMENT width_measure; + +MEASUREMENT width_priority_range;/* Help to normalize */ +MEASUREMENT match_priority_range; + +TALLY states_before_best; +TALLY best_certainties[2]; +TALLY character_widths; /* Width histogram */ + +FILE *priority_file_1; /* Output to cluster */ +FILE *priority_file_2; +FILE *priority_file_3; + +STATE *known_best_state = NULL; /* The right answer */ + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +#define CERTAINTY_BUCKET_SIZE -0.5 +#define CERTAINTY_BUCKETS 40 + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * init_metrics + * + * Set up the appropriate variables to record information about the + * OCR process. Later calls will log the data and save a summary. + **********************************************************************/ +void init_metrics() { + words_chopped1 = 0; + words_chopped2 = 0; + chops_performed1 = 0; + chops_performed2 = 0; + chops_attempted1 = 0; + chops_attempted2 = 0; + + words_segmented1 = 0; + words_segmented2 = 0; + states_timed_out1 = 0; + states_timed_out2 = 0; + segmentation_states1 = 0; + segmentation_states2 = 0; + + save_priorities = 0; + + character_count = 0; + word_count = 0; + chars_classified = 0; + permutation_count = 0; + + states_before_best = new_tally (min (100, num_seg_states)); + + best_certainties[0] = new_tally (CERTAINTY_BUCKETS); + best_certainties[1] = new_tally (CERTAINTY_BUCKETS); +} + +void end_metrics() { + memfree(states_before_best); + memfree(best_certainties[0]); + memfree(best_certainties[1]); + memfree(character_widths); + states_before_best = NULL; + best_certainties[0] = NULL; + best_certainties[1] = NULL; + character_widths = NULL; +} + + +/********************************************************************** + * record_certainty + * + * Maintain a record of the best certainty values achieved on each + * word recognition. + **********************************************************************/ +void record_certainty(float certainty, int pass) { + int bucket; + + if (certainty / CERTAINTY_BUCKET_SIZE < MAXINT) + bucket = (int) (certainty / CERTAINTY_BUCKET_SIZE); + else + bucket = MAXINT; + + inc_tally_bucket (best_certainties[pass - 1], bucket); +} + + +/********************************************************************** + * record_search_status + * + * Record information about each iteration of the search. This data + * is kept in global memory and accumulated over multiple segmenter + * searches. + **********************************************************************/ +void record_search_status(int num_states, int before_best, float closeness) { + inc_tally_bucket(states_before_best, before_best); + + if (first_pass) { + if (num_states == num_seg_states + 1) + states_timed_out1++; + segmentation_states1 += num_states; + words_segmented1++; + } + else { + if (num_states == num_seg_states + 1) + states_timed_out2++; + segmentation_states2 += num_states; + words_segmented2++; + } +} + + +/********************************************************************** + * save_summary + * + * Save the summary information into the file "file.sta". + **********************************************************************/ +void save_summary(INT32 elapsed_time) { + #ifndef SECURE_NAMES + char outfilename[CHARS_PER_LINE]; + FILE *f; + int x; + int total; + + strcpy(outfilename, imagefile); + strcat (outfilename, ".sta"); + f = open_file (outfilename, "w"); + + fprintf (f, INT32FORMAT " seconds elapsed\n", elapsed_time); + fprintf (f, "\n"); + + fprintf (f, "%d characters\n", character_count); + fprintf (f, "%d words\n", word_count); + fprintf (f, "\n"); + + fprintf (f, "%d permutations performed\n", permutation_count); + fprintf (f, "%d characters classified\n", chars_classified); + fprintf (f, "%4.0f%% classification overhead\n", + (float) chars_classified / character_count * 100.0 - 100.0); + fprintf (f, "\n"); + + fprintf (f, "%d words chopped (pass 1) ", words_chopped1); + fprintf (f, " (%0.0f%%)\n", (float) words_chopped1 / word_count * 100); + fprintf (f, "%d chops performed\n", chops_performed1); + fprintf (f, "%d chops attempted\n", chops_attempted1); + fprintf (f, "\n"); + + fprintf (f, "%d words joined (pass 1)", words_segmented1); + fprintf (f, " (%0.0f%%)\n", (float) words_segmented1 / word_count * 100); + fprintf (f, "%d segmentation states\n", segmentation_states1); + fprintf (f, "%d segmentations timed out\n", states_timed_out1); + fprintf (f, "\n"); + + fprintf (f, "%d words chopped (pass 2) ", words_chopped2); + fprintf (f, " (%0.0f%%)\n", (float) words_chopped2 / word_count * 100); + fprintf (f, "%d chops performed\n", chops_performed2); + fprintf (f, "%d chops attempted\n", chops_attempted2); + fprintf (f, "\n"); + + fprintf (f, "%d words joined (pass 2)", words_segmented2); + fprintf (f, " (%0.0f%%)\n", (float) words_segmented2 / word_count * 100); + fprintf (f, "%d segmentation states\n", segmentation_states2); + fprintf (f, "%d segmentations timed out\n", states_timed_out2); + fprintf (f, "\n"); + + total = 0; + iterate_tally (states_before_best, x) + total += (tally_entry (states_before_best, x) * x); + fprintf (f, "segmentations (before best) = %d\n", total); + if (total != 0.0) + fprintf (f, "%4.0f%% segmentation overhead\n", + (float) (segmentation_states1 + segmentation_states2) / + total * 100.0 - 100.0); + fprintf (f, "\n"); + + print_tally (f, "segmentations (before best)", states_before_best); + + iterate_tally (best_certainties[0], x) + cprintf ("best certainty of %8.4f = %4d %4d\n", + x * CERTAINTY_BUCKET_SIZE, + tally_entry (best_certainties[0], x), + tally_entry (best_certainties[1], x)); + + PrintIntMatcherStats(f); + dj_statistics(f); + fclose(f); + #endif +} + + +/********************************************************************** + * record_priorities + * + * If the record mode is set then record the priorities returned by + * each of the priority voters. Save them in a file that is set up for + * doing clustering. + **********************************************************************/ +void record_priorities(SEARCH_RECORD *the_search, + STATE *old_state, + FLOAT32 priority_1, + FLOAT32 priority_2) { + record_samples(priority_1, priority_2); +} + + +/********************************************************************** + * record_samples + * + * Remember the priority samples to summarize them later. + **********************************************************************/ +void record_samples(FLOAT32 match_pri, FLOAT32 width_pri) { + ADD_SAMPLE(match_priority_range, match_pri); + ADD_SAMPLE(width_priority_range, width_pri); +} + + +/********************************************************************** + * reset_width_tally + * + * Create a tally record and initialize it. + **********************************************************************/ +void reset_width_tally() { + character_widths = new_tally (20); + new_measurement(width_measure); + width_measure.num_samples = 158; + width_measure.sum_of_samples = 125.0; + width_measure.sum_of_squares = 118.0; +} + + +#ifndef GRAPHICS_DISABLED +/********************************************************************** + * save_best_state + * + * Save this state away to be compared later. + **********************************************************************/ +void save_best_state(CHUNKS_RECORD *chunks_record) { + STATE state; + SEARCH_STATE chunk_groups; + int num_joints; + + if (save_priorities) { + num_joints = matrix_dimension (chunks_record->ratings) - 1; + + state.part1 = 0xffffffff; + state.part2 = 0xffffffff; + + chunk_groups = bin_to_chunks (&state, num_joints); + display_segmentation (chunks_record->chunks, chunk_groups); + memfree(chunk_groups); + + cprintf ("Enter the correct segmentation > "); + fflush(stdout); + state.part1 = 0; + scanf ("%x", &state.part2); + + chunk_groups = bin_to_chunks (&state, num_joints); + display_segmentation (chunks_record->chunks, chunk_groups); + memfree(chunk_groups); + window_wait(segm_window); /* == 'n') */ + + if (known_best_state) + free_state(known_best_state); + known_best_state = new_state (&state); + } +} +#endif + + +/********************************************************************** + * start_record + * + * Set up everything needed to record the priority voters. + **********************************************************************/ +void start_recording() { + if (save_priorities) { + priority_file_1 = open_file ("Priorities1", "w"); + priority_file_2 = open_file ("Priorities2", "w"); + priority_file_3 = open_file ("Priorities3", "w"); + } +} + + +/********************************************************************** + * stop_recording + * + * Put an end to the priority recording mechanism. + **********************************************************************/ +void stop_recording() { + if (save_priorities) { + fclose(priority_file_1); + fclose(priority_file_2); + fclose(priority_file_3); + } +} diff --git a/wordrec/metrics.h b/wordrec/metrics.h new file mode 100644 index 0000000000..3e85a9133f --- /dev/null +++ b/wordrec/metrics.h @@ -0,0 +1,130 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: metrics.h (Formerly metrics.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 30 17:02:48 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef METRICS_H +#define METRICS_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "measure.h" +#include "bestfirst.h" +#include "states.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int words_chopped1; +extern int words_chopped2; +extern int chops_attempted1; +extern int chops_performed1; +extern int chops_attempted2; +extern int chops_performed2; +extern int permutation_count; + +extern int character_count; +extern int word_count; +extern int chars_classified; + +extern MEASUREMENT width_measure; +extern MEASUREMENT width_priority_range; +extern MEASUREMENT match_priority_range; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void init_metrics(); +void end_metrics(); + +void record_certainty(float certainty, int pass); + +void record_search_status(int num_states, int before_best, float closeness); + +#ifndef SECURE_NAMES +void save_summary(INT32 elapsed_time); +#endif + +void record_priorities(SEARCH_RECORD *the_search, + STATE *old_state, + FLOAT32 priority_1, + FLOAT32 priority_2); + +void record_samples(FLOAT32 match_pri, FLOAT32 width_pri); + +void reset_width_tally(); + +void save_best_state(CHUNKS_RECORD *chunks_record); + +void start_recording(); + +void stop_recording(); + +/* +#if defined(__STDC__) || defined(__cplusplus) || MAC_OR_DOS +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* metrics.c +void init_metrics + _ARGS((void)); + +void record_certainty + _ARGS((float certainty, + int pass)); + +void record_search_status + _ARGS((int num_states, + int before_best, + float closeness)); + +void save_summary + _ARGS((INT32 elapsed_time)); + +void record_priorities + _ARGS((SEARCH_RECORD *the_search, + STATE *old_state, + FLOAT32 priority_1, + FLOAT32 priority_2)); + +void record_samples + _ARGS((FLOAT32 match_pri, + FLOAT32 width_pri)); + +void reset_width_tally + _ARGS((void)); + +void save_best_state + _ARGS((CHUNKS_RECORD *chunks_record)); + +void start_recording + _ARGS((void)); + +void stop_recording + _ARGS((void)); + +#undef _ARGS +*/ +#endif diff --git a/wordrec/mfvars.cpp b/wordrec/mfvars.cpp new file mode 100644 index 0000000000..a3ed224783 --- /dev/null +++ b/wordrec/mfvars.cpp @@ -0,0 +1,51 @@ +/****************************************************************************** + ** Filename: mfvars.c + ** Purpose: Hooks global microfeature variables into the wo system. + ** Author: Dan Johnson + ** History: Fri Jan 12 12:47:20 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#include "blobclass.h" +#include "extract.h" +#include "adaptmatch.h" + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void mfeature_variables() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Install global variables into the wiseowl variable system. + ** Return: none + ** Exceptions: none + ** History: Fri Jan 12 13:17:07 1990, DSJ, Created. + */ + InitBlobClassifierVars(); + InitExtractorVars(); +} /* mfeature_variables */ + + +/*---------------------------------------------------------------------------*/ +void mfeature_init() { +/* + ** Parameters: none + ** Globals: none + ** Operation: none + ** Return: none + ** Exceptions: none + ** History: Fri Jan 12 13:22:41 1990, DSJ, Created. + */ + InitAdaptiveClassifier(); +} /* mfeature_init */ diff --git a/wordrec/mfvars.h b/wordrec/mfvars.h new file mode 100644 index 0000000000..1b40e4883d --- /dev/null +++ b/wordrec/mfvars.h @@ -0,0 +1,27 @@ +/****************************************************************************** + ** Filename: mfvars.h + ** Purpose: Routines to install global variables into wo system. + ** Author: Dan Johnson + ** History: Fri Jan 12 13:23:46 1990, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef MFVARS_H +#define MFVARS_H + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void mfeature_variables(); + +void mfeature_init(); +#endif diff --git a/wordrec/msmenus.cpp b/wordrec/msmenus.cpp new file mode 100644 index 0000000000..201900411c --- /dev/null +++ b/wordrec/msmenus.cpp @@ -0,0 +1,110 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: msmenus.c (Formerly msmenus.c) + * Description: Menu creation and initialization + * Author: Mark Seaman, OCR Technology + * Created: Thu Jul 27 08:59:01 1989 + * Modified: Fri Jul 12 16:04:50 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------*/ +#include +#include + +#include "msmenus.h" +#include "debug.h" +#include "sigmenu.h" +/* includes for init functions */ +#include "plotseg.h" +#include "baseline.h" +#include "bestfirst.h" +#include "split.h" +#include "associate.h" +#include "chop.h" +#include "tordvars.h" +#include "permute.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +int handle_menu_3(); +int handle_menu_4(); +int handle_menu_5(); +int handle_menu_6(); +int handle_menu_7(); +int handle_menu_8(); +int handle_menu_9(); +#endif + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * init_ms_debug + * + * Initialize the traps for handling the splitter debug stuff. + **********************************************************************/ +void init_ms_debug() { + static int first_time = 1; + + if (first_time) { + first_time = 0; + /* Set up the interrupts */ + #ifndef GRAPHICS_DISABLED + #ifndef SECURE_NAMES + AddSignalMenuItem (SIGINT, 3, "Chopper", handle_menu_3); + AddSignalMenuItem (SIGINT, 4, "Text Order", handle_menu_4); + AddSignalMenuItem (SIGINT, 5, "Graphics", handle_menu_5); + AddSignalMenuItem (SIGINT, 6, "Text Display", handle_menu_6); + AddSignalMenuItem (SIGINT, 7, "Similarity Matcher", handle_menu_7); + AddSignalMenuItem (SIGINT, 8, "Context", handle_menu_8); + AddSignalMenuItem (SIGINT, 9, "Joiner", handle_menu_9); + #endif + + init_plotseg(); + init_render_vars(); + #endif + + init_baseline(); + init_bestfirst_vars(); + init_splitter_vars(); + init_associate_vars(); + init_chop(); + + init_textord_vars(); + init_permute(); + } +} + + +/********************************************************************** + * handle_menu_X + * + * Initialize the traps for handling the splitter debug stuff. + **********************************************************************/ +#ifndef GRAPHICS_DISABLED +handle_menu (3, handle_menu_3) +handle_menu (4, handle_menu_4) +handle_menu (5, handle_menu_5) +handle_menu (6, handle_menu_6) +handle_menu (7, handle_menu_7) +handle_menu (8, handle_menu_8) handle_menu (9, handle_menu_9) +#endif diff --git a/wordrec/msmenus.h b/wordrec/msmenus.h new file mode 100644 index 0000000000..e534dc81a7 --- /dev/null +++ b/wordrec/msmenus.h @@ -0,0 +1,45 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: msmenus.h (Formerly msmenus.h) + * Description: Create and initialize menus + * Author: Mark Seaman, OCR Technology + * Created: Mon Sep 24 09:34:21 1990 + * Modified: Mon Sep 24 09:34:34 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef MSMENUS_H +#define MSMENUS_H + +/*--------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------*/ +void init_ms_debug(); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _P(s) s +#else +# define _P(s) () +#endif + +void init_debug + _P((void)); + +#undef _P +*/ +#endif diff --git a/wordrec/olutil.cpp b/wordrec/olutil.cpp new file mode 100644 index 0000000000..b02dc11f85 --- /dev/null +++ b/wordrec/olutil.cpp @@ -0,0 +1,153 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: olutil.c (Formerly olutil.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Fri May 17 13:11:24 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "olutil.h" +#include "structures.h" +#include "blobs.h" +#include "const.h" + +#ifdef __UNIX__ +#include +#endif + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * check_outline_mem + * + * Check the memory allocator for outlines. + **********************************************************************/ +void check_outline_mem() { + TESSLINE *outline; + + outline = newoutline (); + outline->next = newoutline (); + oldoutline (outline->next); + oldoutline(outline); + + outline = newoutline (); + outline->next = newoutline (); + oldoutline (outline->next); + oldoutline(outline); +} + + +/********************************************************************** + * correct_blob_order + * + * Check to see if the blobs are in the correct order. If they are not + * then swap which outlines are attached to which blobs. + **********************************************************************/ +void correct_blob_order(TBLOB *blob1, TBLOB *blob2) { + TPOINT origin1; + TPOINT origin2; + TESSLINE *temp; + + blob_origin(blob1, &origin1); + blob_origin(blob2, &origin2); + + if (origin1.x > origin2.x) { + temp = blob2->outlines; + blob2->outlines = blob1->outlines; + blob1->outlines = temp; + } +} + + +/********************************************************************** + * eliminate_duplicate_outlines + * + * Find and delete any duplicate outline records in this blob. + **********************************************************************/ +void eliminate_duplicate_outlines(TBLOB *blob) { + TESSLINE *outline; + TESSLINE *other_outline; + TESSLINE *last_outline; + + for (outline = blob->outlines; outline; outline = outline->next) { + + for (last_outline = outline, other_outline = outline->next; + other_outline; + last_outline = other_outline, other_outline = other_outline->next) { + + if (same_outline_bounds (outline, other_outline)) { + last_outline->next = other_outline->next; + oldoutline(other_outline); + other_outline = last_outline; + } + } + } +} + + +/********************************************************************** + * setup_outline + * + * Create a new outline structure from this + **********************************************************************/ +void setup_outline(TESSLINE *outline) { + register EDGEPT *this_edge; + register int minx = MAXINT; + register int miny = MAXINT; + register int maxx = -MAXINT; + register int maxy = -MAXINT; + + /* Find boundaries */ + this_edge = outline->loop; + do { + if (this_edge->pos.x < minx) + minx = this_edge->pos.x; + if (this_edge->pos.y < miny) + miny = this_edge->pos.y; + if (this_edge->pos.x > maxx) + maxx = this_edge->pos.x; + if (this_edge->pos.y > maxy) + maxy = this_edge->pos.y; + this_edge = this_edge->next; + } + while (this_edge != outline->loop); + /* Reset bounds */ + outline->topleft.x = minx; + outline->topleft.y = maxy; + outline->botright.x = maxx; + outline->botright.y = miny; +} + + +/********************************************************************** + * setup_blob_outlines + * + * Set up each of the outlines in this blob. + **********************************************************************/ +void setup_blob_outlines(TBLOB *blob) { + TESSLINE *outline; + + for (outline = blob->outlines; outline; outline = outline->next) { + setup_outline(outline); + } +} diff --git a/wordrec/olutil.h b/wordrec/olutil.h new file mode 100644 index 0000000000..9750a0a635 --- /dev/null +++ b/wordrec/olutil.h @@ -0,0 +1,128 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: olutil.h (Formerly olutil.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Wed Jul 10 14:21:55 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef OLUTIL_H +#define OLUTIL_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "tessclas.h" +#include "general.h" + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * is_inside_angle + * + * Return true if the edgept supplied as input is an inside angle. This + * is determined by the angular change of the vectors from point to + * point. + + **********************************************************************/ + +#define is_inside_angle(pt) \ +(angle_change ((pt)->prev, (pt), (pt)->next) < inside_angle) + +/********************************************************************** + * point_in_outline + * + * Check to see if this point falls within the bounding box of this + * outline. Note that this does not totally ensure that the edge + * point falls on this outline. + **********************************************************************/ + +#define point_in_outline(p,o) \ +((p)->pos.x >= (o)->topleft.x && \ + (p)->pos.y <= (o)->topleft.y && \ + (p)->pos.x <= (o)->botright.x && \ + (p)->pos.y >= (o)->botright.y) \ + + +/********************************************************************** + * same_outline_bounds + * + * Return TRUE if these two outlines have the same bounds. + **********************************************************************/ + +#define same_outline_bounds(outline,other_outline) \ +(outline->topleft.x == other_outline->topleft.x && \ + outline->topleft.y == other_outline->topleft.y && \ + outline->botright.x == other_outline->botright.x && \ + outline->botright.y == other_outline->botright.y) \ + + +/********************************************************************** + * weighted_edgept_dist + * + * Return the distance (squared) between the two edge points. + **********************************************************************/ + +#define weighted_edgept_dist(p1,p2,x_y_weight) \ +(((p1)->pos.x - (p2)->pos.x) * \ + ((p1)->pos.x - (p2)->pos.x) * x_y_weight + \ + ((p1)->pos.y - (p2)->pos.y) * \ + ((p1)->pos.y - (p2)->pos.y)) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void check_outline_mem(); + +void correct_blob_order(TBLOB *blob1, TBLOB *blob2); + +void eliminate_duplicate_outlines(TBLOB *blob); + +void setup_outline(TESSLINE *outline); + +void setup_blob_outlines(TBLOB *blob); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* olutil.c +void check_outline_mem + _ARGS((void)); + +void correct_blob_order + _ARGS((BLOB *blob1, + BLOB *blob2)); + +void eliminate_duplicate_outlines + _ARGS((BLOB *blob)); + +void setup_outline + _ARGS((TESSLINE *outline)); + +void setup_blob_outlines + _ARGS((BLOB *blob)); + +#undef _ARGS +*/ +#endif diff --git a/wordrec/outlines.cpp b/wordrec/outlines.cpp new file mode 100644 index 0000000000..80108dc17a --- /dev/null +++ b/wordrec/outlines.cpp @@ -0,0 +1,172 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: outlines.c (Formerly outlines.c) + * Description: Combinatorial Splitter + * Author: Mark Seaman, OCR Technology + * Created: Thu Jul 27 08:59:01 1989 + * Modified: Wed Jul 10 14:56:49 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ******************************************************************************** + * Revision 1.2 89/09/15 09:24:41 09:24:41 marks (Mark Seaman) + * First released version of Combinatorial splitter code +**/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "outlines.h" + +#ifdef __UNIX__ +#include +#endif + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * crosses_outline + * + * Check to see if this line crosses over this outline. If it does + * return TRUE. + **********************************************************************/ +int crosses_outline(EDGEPT *p0, /* Start of line */ + EDGEPT *p1, /* End of line */ + EDGEPT *outline) { /* Outline to check */ + EDGEPT *pt = outline; + do { + if (is_crossed (p0->pos, p1->pos, pt->pos, pt->next->pos)) + return (TRUE); + pt = pt->next; + } + while (pt != outline); + return (FALSE); +} + + +/********************************************************************** + * is_crossed + * + * Return TRUE when the two line segments cross each other. Find out + * where the projected lines would cross and then check to see if the + * point of intersection lies on both of the line segments. If it does + * then these two segments cross. + **********************************************************************/ +int is_crossed(TPOINT a0, TPOINT a1, TPOINT b0, TPOINT b1) { + int b0a1xb0b1, b0b1xb0a0; + int a1b1xa1a0, a1a0xa1b0; + + TPOINT b0a1, b0a0, a1b1, b0b1, a1a0; + + b0a1.x = a1.x - b0.x; + b0a0.x = a0.x - b0.x; + a1b1.x = b1.x - a1.x; + b0b1.x = b1.x - b0.x; + a1a0.x = a0.x - a1.x; + b0a1.y = a1.y - b0.y; + b0a0.y = a0.y - b0.y; + a1b1.y = b1.y - a1.y; + b0b1.y = b1.y - b0.y; + a1a0.y = a0.y - a1.y; + + b0a1xb0b1 = CROSS (b0a1, b0b1); + b0b1xb0a0 = CROSS (b0b1, b0a0); + a1b1xa1a0 = CROSS (a1b1, a1a0); + /*a1a0xa1b0=CROSS(a1a0,a1b0); */ + a1a0xa1b0 = -CROSS (a1a0, b0a1); + + return (b0a1xb0b1 > 0 && b0b1xb0a0 > 0 + || b0a1xb0b1 < 0 && b0b1xb0a0 < 0) + && (a1b1xa1a0 > 0 && a1a0xa1b0 > 0 || a1b1xa1a0 < 0 && a1a0xa1b0 < 0); +} + + +/********************************************************************** + * is_same_edgept + * + * Return true if the points are identical. + **********************************************************************/ +int is_same_edgept(EDGEPT *p1, EDGEPT *p2) { + return (p1 == p2); +} + + +/********************************************************************** + * near_point + * + * Find the point on a line segment that is closest to a point not on + * the line segment. Return that point. + **********************************************************************/ +EDGEPT *near_point(EDGEPT *point, EDGEPT *line_pt_0, EDGEPT *line_pt_1) { + TPOINT p; + + float slope; + float intercept; + + float x0 = line_pt_0->pos.x; + float x1 = line_pt_1->pos.x; + float y0 = line_pt_0->pos.y; + float y1 = line_pt_1->pos.y; + + if (x0 == x1) { + /* Handle vertical line */ + p.x = (INT16) x0; + p.y = point->pos.y; + } + else { + /* Slope and intercept */ + slope = (y0 - y1) / (x0 - x1); + intercept = y1 - x1 * slope; + + /* Find perpendicular */ + p.x = (INT16) ((point->pos.x + (point->pos.y - intercept) * slope) / + (slope * slope + 1)); + p.y = (INT16) (slope * p.x + intercept); + } + + if (is_on_line (p, line_pt_0->pos, line_pt_1->pos) && + (!same_point (p, line_pt_0->pos)) && (!same_point (p, line_pt_1->pos))) + /* Intersection on line */ + return (make_edgept (p.x, p.y, line_pt_1, line_pt_0)); + else /* Intersection not on line */ + return (closest (point, line_pt_0, line_pt_1)); +} + + +/********************************************************************** + * reverse_outline + * + * Change the direction of the outline. If it was clockwise make it + * counter-clockwise and vice versa. Do this by swapping each of the + * next and prev fields of each edge point. + **********************************************************************/ +void reverse_outline(EDGEPT *outline) { + EDGEPT *edgept = outline; + EDGEPT *temp; + + do { + /* Swap next and prev */ + temp = edgept->prev; + edgept->prev = edgept->next; + edgept->next = temp; + /* Set up vec field */ + edgept->vec.x = edgept->next->pos.x - edgept->pos.x; + edgept->vec.y = edgept->next->pos.y - edgept->pos.y; + + edgept = edgept->prev; /* Go to next point */ + } + while (edgept != outline); +} diff --git a/wordrec/outlines.h b/wordrec/outlines.h new file mode 100644 index 0000000000..952853100e --- /dev/null +++ b/wordrec/outlines.h @@ -0,0 +1,148 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: outlines.h (Formerly outlines.h) + * Description: Combinatorial Splitter + * Author: Mark Seaman, OCR Technology + * Created: Thu Jul 27 11:27:55 1989 + * Modified: Wed May 15 17:28:47 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef OUTLINES_H +#define OUTLINES_H + +#include "blobs.h" +#include "chop.h" + +#include + +/*---------------------------------------------------------------------- + C o n s t a n t s +----------------------------------------------------------------------*/ +#define LARGE_DISTANCE 100000 /* Used for closest dist */ +#define MIN_BLOB_SIZE 10 /* Big units */ +#define MAX_ASPECT_RATIO 2.5 /* Widest character */ + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * same_point + * + * Return TRUE if the point values are the same. The parameters must + * be of type POINT. + **********************************************************************/ +#define same_point(p1,p2) \ + ((abs (p1.x - p2.x) < same_distance) && \ + (abs (p1.y - p2.y) < same_distance)) + +/********************************************************************** + * dist_square + * + * Return the square of the distance between these two points. The + * parameters must be of type POINT. + **********************************************************************/ + +#define dist_square(p1,p2) \ + ((p2.x - p1.x) * (p2.x - p1.x) + \ + (p2.y - p1.y) * (p2.y - p1.y)) + +/********************************************************************** + * closest + * + * The expression provides the EDGEPT that is closest to the point in + * question. All three parameters must be of type EDGEPT. + **********************************************************************/ + +#define closest(test_p,p1,p2) \ +(p1 ? \ + (p2 ? \ + ((dist_square (test_p->pos, p1->pos) < \ + dist_square (test_p->pos, p2->pos)) ? \ + p1 : \ + p2) : \ + p1) : \ + p2) + +/********************************************************************** + * edgept_dist + * + * Return the distance (squared) between the two edge points. + **********************************************************************/ + +#define edgept_dist(p1,p2) \ +(dist_square ((p1)->pos, (p2)->pos)) + +/********************************************************************** + * is_exterior_point + * + * Return TRUE if the point supplied is an exterior projection from the + * outline. + **********************************************************************/ + +#define is_exterior_point(edge,point) \ +(same_point (edge->prev->pos, point->pos) || \ + same_point (edge->next->pos, point->pos) || \ + (angle_change (edge->prev, edge, edge->next) - \ + angle_change (edge->prev, edge, point) > 20)) + +/********************************************************************** + * is_equal + * + * Return TRUE if the POINTs are equal. + **********************************************************************/ + +#define is_equal(p1,p2) \ +(((p1).x == (p2).x) && ((p1).y == (p2).y)) + +/********************************************************************** + * is_on_line + * + * Return TRUE if the point is on the line segment between the two end + * points. The two end points are included as part of the line. The + * parameters must be of type POINT. + **********************************************************************/ + +#define is_on_line(p,p0,p1) \ + (within_range ((p).x, (p0).x, (p1).x) && \ + within_range ((p).y, (p0).y, (p1).y)) + +/********************************************************************** + * within_range + * + * Return TRUE if the first number is in between the second two numbers. + * Return FALSE otherwise. + **********************************************************************/ + +#define within_range(x,x0,x1) \ + (((x0 <= x) && (x <= x1)) || ((x1 <= x) && (x <= x0))) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +int crosses_outline(EDGEPT *p0, /* Start of line */ + EDGEPT *p1, /* End of line */ + EDGEPT *outline); + +int is_crossed(TPOINT a0, TPOINT a1, TPOINT b0, TPOINT b1); + +int is_same_edgept(EDGEPT *p1, EDGEPT *p2); + +EDGEPT *near_point(EDGEPT *point, EDGEPT *line_pt_0, EDGEPT *line_pt_1); + +void reverse_outline(EDGEPT *outline); +#endif diff --git a/wordrec/pieces.cpp b/wordrec/pieces.cpp new file mode 100644 index 0000000000..01f5c9ab3a --- /dev/null +++ b/wordrec/pieces.cpp @@ -0,0 +1,410 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: pieces.c (Formerly pieces.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon May 20 12:12:35 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "pieces.h" +#include "plotseg.h" +#include "hideedge.h" +#include "wordclass.h" +#include "freelist.h" +#include "blobs.h" +#include "matchtab.h" + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * set_bounds_entry + * + * Set the value of the entry in an array of bounds elements. + **********************************************************************/ + +#define set_bounds_entry(array,index,top_left,bot_right) \ +((array)[index].topleft = (top_left), \ +(array)[index].botright = (bot_right)) \ + + +/********************************************************************** + * get_bounds_entry + * + * Get the value of the entry in an array of bounds elements. + **********************************************************************/ + +#define get_bounds_entry(array,index,top_left,bot_right) \ +((top_left) = (array)[index].topleft, \ +(bot_right) = (array)[index].botright) \ + + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * break_pieces + * + * Break up the blobs in this chain so that they are all independent. + * This operation should undo the affect of join_pieces. + **********************************************************************/ +void break_pieces(TBLOB *blobs, SEAMS seams, INT16 start, INT16 end) { + TESSLINE *outline = blobs->outlines; + TBLOB *next_blob; + INT16 x; + + for (x = start; x < end; x++) + reveal_seam ((SEAM *) array_value (seams, x)); + + next_blob = blobs->next; + + while (outline && next_blob) { + if (outline->next == next_blob->outlines) { + outline->next = NULL; + outline = next_blob->outlines; + next_blob = next_blob->next; + } + else { + outline = outline->next; + } + } +} + + +/********************************************************************** + * join_pieces + * + * Join a group of base level pieces into a single blob that can then + * be classified. + **********************************************************************/ +void join_pieces(TBLOB *piece_blobs, SEAMS seams, INT16 start, INT16 end) { + TBLOB *next_blob; + TBLOB *blob; + INT16 x; + TESSLINE *outline; + SEAM *seam; + + for (x = 0, blob = piece_blobs; x < start; x++) + blob = blob->next; + next_blob = blob->next; + outline = blob->outlines; + if (!outline) + return; + + while (x < end) { + seam = (SEAM *) array_value (seams, x); + if (x - seam->widthn >= start && x + seam->widthp < end) + hide_seam(seam); + while (outline->next) + outline = outline->next; + outline->next = next_blob->outlines; + next_blob = next_blob->next; + + x++; + } +} + + +/********************************************************************** + * hide_seam + * + * Change the edge points that are referenced by this seam to make + * them hidden edges. + **********************************************************************/ +void hide_seam(SEAM *seam) { + if (seam == NULL || seam->split1 == NULL) + return; + hide_edge_pair (seam->split1->point1, seam->split1->point2); + + if (seam->split2 == NULL) + return; + hide_edge_pair (seam->split2->point1, seam->split2->point2); + + if (seam->split3 == NULL) + return; + hide_edge_pair (seam->split3->point1, seam->split3->point2); +} + + +/********************************************************************** + * hide_edge_pair + * + * Change the edge points that are referenced by this seam to make + * them hidden edges. + **********************************************************************/ +void hide_edge_pair(EDGEPT *pt1, EDGEPT *pt2) { + EDGEPT *edgept; + + edgept = pt1; + do { + hide_edge(edgept); + edgept = edgept->next; + } + while (!exact_point (edgept, pt2) && edgept != pt1); + if (edgept == pt1) { + /* cprintf("Hid entire outline at (%d,%d)!!\n", + edgept->pos.x,edgept->pos.y); */ + } + edgept = pt2; + do { + hide_edge(edgept); + edgept = edgept->next; + } + while (!exact_point (edgept, pt1) && edgept != pt2); + if (edgept == pt2) { + /* cprintf("Hid entire outline at (%d,%d)!!\n", + edgept->pos.x,edgept->pos.y); */ + } +} + + +/********************************************************************** + * reveal_seam + * + * Change the edge points that are referenced by this seam to make + * them hidden edges. + **********************************************************************/ +void reveal_seam(SEAM *seam) { + if (seam == NULL || seam->split1 == NULL) + return; + reveal_edge_pair (seam->split1->point1, seam->split1->point2); + + if (seam->split2 == NULL) + return; + reveal_edge_pair (seam->split2->point1, seam->split2->point2); + + if (seam->split3 == NULL) + return; + reveal_edge_pair (seam->split3->point1, seam->split3->point2); +} + + +/********************************************************************** + * reveal_edge_pair + * + * Change the edge points that are referenced by this seam to make + * them hidden edges. + **********************************************************************/ +void reveal_edge_pair(EDGEPT *pt1, EDGEPT *pt2) { + EDGEPT *edgept; + + edgept = pt1; + do { + reveal_edge(edgept); + edgept = edgept->next; + } + while (!exact_point (edgept, pt2) && edgept != pt1); + if (edgept == pt1) { + /* cprintf("Hid entire outline at (%d,%d)!!\n", + edgept->pos.x,edgept->pos.y); */ + } + edgept = pt2; + do { + reveal_edge(edgept); + edgept = edgept->next; + } + while (!exact_point (edgept, pt1) && edgept != pt2); + if (edgept == pt2) { + /* cprintf("Hid entire outline at (%d,%d)!!\n", + edgept->pos.x,edgept->pos.y); */ + } +} + + +/********************************************************************** + * bounds_of_piece + * + * Find the bounds of the piece that will be created by joining the + * requested collection of pieces together. + **********************************************************************/ +void bounds_of_piece(BOUNDS_LIST bounds, + INT16 start, + INT16 end, + TPOINT *extreme_tl, + TPOINT *extreme_br) { + TPOINT topleft; + TPOINT botright; + INT16 x; + + get_bounds_entry(bounds, start, *extreme_tl, *extreme_br); + + for (x = start + 1; x <= end; x++) { + get_bounds_entry(bounds, x, topleft, botright); + + extreme_tl->x = min (topleft.x, extreme_tl->x); + extreme_tl->y = max (topleft.y, extreme_tl->y); + extreme_br->x = max (botright.x, extreme_br->x); + extreme_br->y = min (botright.y, extreme_br->y); + } +} + + +/********************************************************************** + * classify_piece + * + * Create a larger piece from a collection of smaller ones. Classify + * it and return the results. Take the large piece apart to leave + * the collection of small pieces un modified. + **********************************************************************/ +CHOICES classify_piece(TBLOB *pieces, + SEAMS seams, + INT16 start, + INT16 end, + INT32 fx, + STATE *this_state, + STATE *best_state, + INT32 pass, + INT32 blob_index) { + STATE current_state; + CHOICES choices; + TBLOB *pblob; + TBLOB *blob; + TBLOB *nblob; + INT16 x; + SEARCH_STATE chunk_groups; + + set_n_ones (¤t_state, array_count (seams)); + + join_pieces(pieces, seams, start, end); + for (blob = pieces, pblob = NULL, x = 0; x < start; x++) { + pblob = blob; + blob = blob->next; + } + for (nblob = blob->next; x < end; x++) + nblob = nblob->next; + choices = classify_blob (pblob, blob, nblob, NULL, fx, "pieces:", White, + this_state, best_state, pass, blob_index); + + break_pieces(blob, seams, start, end); +#ifndef GRAPHICS_DISABLED + if (display_segmentations > 2) { + chunk_groups = bin_to_chunks (¤t_state, array_count (seams)); + display_segmentation(pieces, chunk_groups); + window_wait(segm_window); + memfree(chunk_groups); + } +#endif + + return (choices); +} + + +/********************************************************************** + * get_piece_rating + * + * Check to see if this piece has already been classified. If it has + * return that rating. Otherwise build the piece from the smaller + * pieces, classify it, store the rating for later, and take the piece + * apart again. + **********************************************************************/ +CHOICES get_piece_rating(MATRIX ratings, + TBLOB *blobs, + SEAMS seams, + INT16 start, + INT16 end, + INT32 fx, + STATE *this_state, + STATE *best_state, + INT32 pass, + INT32 blob_index) { + CHOICES choices; + + choices = matrix_get (ratings, start, end); + if (choices == NOT_CLASSIFIED) { + choices = + classify_piece(blobs, + seams, + start, + end, + fx, + this_state, + best_state, + pass, + blob_index); + matrix_put(ratings, start, end, choices); + } + return (choices); +} + + +/********************************************************************** + * record_blob_bounds + * + * Set up and initialize an array that holds the bounds of a set of + * blobs. + **********************************************************************/ +BOUNDS_LIST record_blob_bounds(TBLOB *blobs) { + TBLOB *blob; + BOUNDS_LIST bounds; + TPOINT topleft; + TPOINT botright; + INT16 x = 0; + + bounds = (BOUNDS_LIST) memalloc (count_blobs (blobs) * sizeof (BOUNDS)); + + iterate_blobs(blob, blobs) { + blob_bounding_box(blob, &topleft, &botright); + set_bounds_entry(bounds, x, topleft, botright); + x++; + } + return (bounds); +} + + +/********************************************************************** + * record_piece_ratings + * + * Save the choices for all the pieces that have been classified into + * a matrix that can be used to look them up later. A two dimensional + * matrix is created. The indices correspond to the starting and + * ending initial piece number. + **********************************************************************/ +MATRIX record_piece_ratings(TBLOB *blobs) { + BOUNDS_LIST bounds; + INT16 num_blobs; + INT16 x; + INT16 y; + TPOINT tp_topleft; + TPOINT tp_botright; + unsigned int topleft; + unsigned int botright; + MATRIX ratings; + CHOICES choices; + + bounds = record_blob_bounds (blobs); + num_blobs = count_blobs (blobs); + ratings = create_matrix (num_blobs); + + for (x = 0; x < num_blobs; x++) { + for (y = x; y < num_blobs; y++) { + bounds_of_piece(bounds, x, y, &tp_topleft, &tp_botright); + topleft = *(unsigned int *) &tp_topleft; + botright = *(unsigned int *) &tp_botright; + choices = get_match_by_bounds (topleft, botright); + if (choices != NIL) { + matrix_put(ratings, x, y, choices); + } + } + } + memfree(bounds); + return (ratings); +} diff --git a/wordrec/pieces.h b/wordrec/pieces.h new file mode 100644 index 0000000000..38671cd735 --- /dev/null +++ b/wordrec/pieces.h @@ -0,0 +1,154 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: pieces.h (Formerly pieces.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Apr 30 11:49:11 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef PIECES_H +#define PIECES_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "matrix.h" +#include "seam.h" +#include "states.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef struct +{ /* BOUNDS */ + TPOINT topleft; + TPOINT botright; +} BOUNDS; + +typedef BOUNDS *BOUNDS_LIST; /* BOUNDS_LIST */ + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void break_pieces(TBLOB *blobs, SEAMS seams, INT16 start, INT16 end); + +void join_pieces(TBLOB *piece_blobs, SEAMS seams, INT16 start, INT16 end); + +void hide_seam(SEAM *seam); + +void hide_edge_pair(EDGEPT *pt1, EDGEPT *pt2); + +void reveal_seam(SEAM *seam); + +void reveal_edge_pair(EDGEPT *pt1, EDGEPT *pt2); + +void bounds_of_piece(BOUNDS_LIST bounds, + INT16 start, + INT16 end, + TPOINT *extreme_tl, + TPOINT *extreme_br); + +CHOICES classify_piece(TBLOB *pieces, + SEAMS seams, + INT16 start, + INT16 end, + INT32 fx, + STATE *this_state, + STATE *best_state, + INT32 pass, + INT32 blob_index); + +CHOICES get_piece_rating(MATRIX ratings, + TBLOB *blobs, + SEAMS seams, + INT16 start, + INT16 end, + INT32 fx, + STATE *this_state, + STATE *best_state, + INT32 pass, + INT32 blob_index); + +BOUNDS_LIST record_blob_bounds(TBLOB *blobs); + +MATRIX record_piece_ratings(TBLOB *blobs); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* pieces.c * +void break_pieces + _ARGS((BLOB *blobs, + SEAMS seams)); + +void join_pieces + _ARGS((BLOB *piece_blobs, + SEAMS seams, + INT16 start, + INT16 end)); + +void hide_seam + _ARGS((SEAM *seam)); + +void reveal_seam + _ARGS((SEAM *seam)); + +void bounds_of_piece + _ARGS((BOUNDS_LIST bounds, + INT16 start, + INT16 end, + TPOINT *extreme_tl, + TPOINT *extreme_br)); + +CHOICES classify_piece + _ARGS((BLOB *pieces, + SEAMS seams, + INT16 start, + INT16 end, + INT32 fx, + STATE* this_state, + STATE* best_state, + INT32 pass, + INT32 blob_index)); + +CHOICES get_piece_rating + _ARGS((MATRIX ratings, + BLOB *blobs, + SEAMS seams, + INT16 start, + INT16 end, + INT32 fx, + STATE* this_state, + STATE* best_state, + INT32 pass, + INT32 blob_index)); + +BOUNDS_LIST record_blob_bounds + _ARGS((BLOB *blobs)); + +MATRIX record_piece_ratings + _ARGS((BLOB *blobs)); + +#undef _ARGS +*/ +#endif diff --git a/wordrec/plotedges.cpp b/wordrec/plotedges.cpp new file mode 100644 index 0000000000..a457335bd8 --- /dev/null +++ b/wordrec/plotedges.cpp @@ -0,0 +1,130 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: plotedges.c (Formerly plotedges.c) + * Description: Graphics routines for "Edges" and "Outlines" windows + * Author: Mark Seaman, OCR Technology + * Created: Fri Jul 28 13:14:48 1989 + * Modified: Tue Jul 9 17:22:22 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifdef __UNIX__ +#include +#endif + +#include "plotedges.h" +#include "render.h" +#include "split.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +void *edge_window = NULL; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * display_edgepts + * + * Macro to display edge points in a window. + **********************************************************************/ +void display_edgepts(LIST outlines) { + void *window; + /* Set up window */ + if (edge_window == NULL) { + edge_window = c_create_window ("Edges", 750, 150, + 400, 128, -400.0, 400.0, 0.0, 256.0); + } + else { + c_clear_window(edge_window); + } + /* Render the outlines */ + window = edge_window; + /* Reclaim old memory */ + iterate(outlines) { + render_edgepts (window, (EDGEPT *) first (outlines), White); + } +} + + +/********************************************************************** + * draw_blob_edges + * + * Display the edges of this blob in the edges window. + **********************************************************************/ +void draw_blob_edges(TBLOB *blob) { + TESSLINE *ol; + LIST edge_list = NIL; + + if (display_splits) { + for (ol = blob->outlines; ol != NULL; ol = ol->next) + push_on (edge_list, ol->loop); + display_edgepts(edge_list); + destroy(edge_list); + } +} + + +/********************************************************************** + * mark_outline + * + * Make a mark on the edges window at a particular location. + **********************************************************************/ +void mark_outline(EDGEPT *edgept) { /* Start of point list */ + void *window = edge_window; + float x = edgept->pos.x; + float y = edgept->pos.y; + + c_line_color_index(window, Red); + c_move(window, x, y); + + x -= 4; + y -= 12; + c_draw(window, x, y); + + x -= 2; + y += 4; + c_draw(window, x, y); + + x -= 4; + y += 2; + c_draw(window, x, y); + + x += 10; + y += 6; + c_draw(window, x, y); + + c_make_current(window); +} + + +/********************************************************************** + * mark_split + * + * Set up the marks list to be displayed in subsequent updates and draw + * the marks in the current window. The marks are stored in the second + * sublist. The first sublist is left unmodified. + **********************************************************************/ +void mark_split(SPLIT *split) { + void *window = edge_window; + + c_line_color_index(window, Green); + c_move (window, (float) split->point1->pos.x, (float) split->point1->pos.y); + c_draw (window, (float) split->point2->pos.x, (float) split->point2->pos.y); + c_make_current(window); +} diff --git a/wordrec/plotedges.h b/wordrec/plotedges.h new file mode 100644 index 0000000000..be864a7531 --- /dev/null +++ b/wordrec/plotedges.h @@ -0,0 +1,71 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: plotedges.h (Formerly plotedges.h) + * Description: Convert the various data type into line lists + * Author: Mark Seaman, OCR Technology + * Created: Fri Jul 28 13:14:48 1989 + * Modified: Mon May 13 09:34:51 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef PLOTEDGES_H +#define PLOTEDGES_H + +#include "callcpp.h" +#include "oldlist.h" +#include "tessclas.h" +#include "split.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern void *edge_window; /* Window for edges */ + +/*---------------------------------------------------------------------- + Macros +----------------------------------------------------------------------*/ +/********************************************************************** + * update_edge_window + * + * Refresh the display of the edge window. + **********************************************************************/ +#define update_edge_window() \ +if (display_splits) { \ + c_make_current (edge_window); \ +} \ + + +/********************************************************************** + * edge_window_wait + * + * Wait for someone to click in the edges window. + **********************************************************************/ + +#define edge_window_wait() \ +if (display_splits) window_wait (edge_window) + +/*---------------------------------------------------------------------- + F u n c t i o n s +---------------------------------------------------------------------*/ +void display_edgepts(LIST outlines); + +void draw_blob_edges(TBLOB *blob); + +void mark_outline(EDGEPT *edgept); + +void mark_split(SPLIT *split); +#endif diff --git a/wordrec/plotseg.cpp b/wordrec/plotseg.cpp new file mode 100644 index 0000000000..5901cf0e35 --- /dev/null +++ b/wordrec/plotseg.cpp @@ -0,0 +1,112 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: plotseg.c (Formerly plotseg.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Fri Apr 26 10:03:05 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "plotseg.h" +#include "callcpp.h" +#include "tessclas.h" +#include "blobs.h" +#include "debug.h" +#include "const.h" +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +void *segm_window = NULL; + +make_int_var (display_segmentations, 0, make_display_seg, +9, 2, toggle_segmentations, "Display Segmentations"); + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * display_segmentation + * + * Display all the words on the page into a window. + **********************************************************************/ +void display_segmentation(TBLOB *chunks, SEARCH_STATE segmentation) { + void *window; + + /* Destroy old data */ + /* If no window create it */ + if (segm_window == NULL) { + segm_window = c_create_window ("Segmentation", 5, 10, + 500, 128, -1000.0, 1000.0, 0.0, 256.0); + } + else { + c_clear_window(segm_window); + } + + window = segm_window; + + render_segmentation(window, chunks, segmentation); + /* Put data in the window */ + c_make_current(window); +} + + +/********************************************************************** + * init_plotseg + * + * Intialize the plotseg control variables. + **********************************************************************/ +void init_plotseg() { + make_display_seg(); +} + + +/********************************************************************** + * render_segmentation + * + * Create a list of line segments that represent the list of chunks + * using the correct segmentation that was supplied as input. + **********************************************************************/ +void render_segmentation(void *window, + TBLOB *chunks, + SEARCH_STATE segmentation) { + TPOINT origin; + TBLOB *blob; + C_COL color = Black; + int char_num = -1; + int chunks_left = 0; + + blobs_origin(chunks, &origin); + + iterate_blobs(blob, chunks) { + + if (chunks_left-- == 0) { + color = color_list[++char_num % NUM_COLORS]; + + if (char_num < segmentation[0]) + chunks_left = segmentation[char_num + 1]; + else + chunks_left = MAXINT; + } + render_outline (window, blob->outlines, origin, color); + } +} diff --git a/wordrec/plotseg.h b/wordrec/plotseg.h new file mode 100644 index 0000000000..6204cd6bee --- /dev/null +++ b/wordrec/plotseg.h @@ -0,0 +1,73 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: plotseg.h (Formerly plotseg.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Fri Apr 26 10:03:32 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef PLOTSEG_H +#define PLOTSEG_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "states.h" +#include "render.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern void *segm_window; +extern int display_segmentations;/* Display Segmentations */ + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void display_segmentation(TBLOB *chunks, SEARCH_STATE segmentation); + +void init_plotseg(); + +void render_segmentation(void *window, + TBLOB *chunks, + SEARCH_STATE segmentation); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* plotseg.c +void display_segmentation + _ARGS((BLOB *chunks, + SEARCH_STATE segmentation)); + +void init_plotseg + _ARGS((void)); + +void render_segmentation + _ARGS((X_WINDOW *window, + BLOB *chunks, + SEARCH_STATE segmentation)); + +#undef _ARGS +*/ +#endif diff --git a/wordrec/render.cpp b/wordrec/render.cpp new file mode 100644 index 0000000000..e6a60df735 --- /dev/null +++ b/wordrec/render.cpp @@ -0,0 +1,158 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: render.c (Formerly render.c) + * Description: Convert the various data type into line lists + * Author: Mark Seaman, OCR Technology + * Created: Fri Jul 28 13:14:48 1989 + * Modified: Mon Jul 15 10:23:37 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#include "render.h" +#include "blobs.h" + +#ifdef __UNIX__ +#include +#endif +#include + +#include "vecfuncs.h" +#include "debug.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +void *blob_window = NULL; + +C_COL color_list[] = { + Red, Cyan, Yellow, Blue, Green, White +}; + +make_toggle_var (display_all_blobs, 0, make_disp_all_blobs, +5, 1, toggle_blobs, "Display Blobs"); + +make_toggle_var (display_all_words, 0, make_disp_all_words, +5, 2, toggle_wdisp, "Display Words"); + +make_toggle_var (blob_pause, 0, make_blob_pause, +5, 6, toggle_pause, "Blob pause"); + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * display_blob + * + * Macro to display blob in a window. + **********************************************************************/ +void display_blob(TBLOB *blob, C_COL color) { + void *window; + TPOINT origin; + /* Size of drawable */ + if (blob_window == NULL) { + blob_window = c_create_window ("Blobs", 520, 10, + 500, 128, -1000.0, 1000.0, 0.0, 256.0); + } + else { + c_clear_window(blob_window); + } + + window = blob_window; + /* Render blob */ + blob_origin(blob, &origin); + + render_blob(window, blob, origin, color); + /* Default zoom */ + c_make_current(window); +} + + +/********************************************************************** + * init_render_vars + * + * Initialize the render graphics menu items. + **********************************************************************/ +void init_render_vars() { + make_disp_all_blobs(); + make_disp_all_words(); + make_blob_pause(); +} + + +/********************************************************************** + * render_blob + * + * Create a list of line segments that represent the expanded outline + * that was supplied as input. + **********************************************************************/ +void render_blob(void *window, TBLOB *blob, TPOINT origin, C_COL color) { + /* No outline */ + if (!blob) + return; + + render_outline (window, blob->outlines, origin, color); +} + + +/********************************************************************** + * render_edgepts + * + * Create a list of line segments that represent the expanded outline + * that was supplied as input. + **********************************************************************/ +void render_edgepts(void *window, EDGEPT *edgept, C_COL color) { + float x = edgept->pos.x; + float y = edgept->pos.y; + EDGEPT *this_edge = edgept; + + if (!edgept) + return; + + c_line_color_index(window, color); + c_move(window, x, y); + do { + this_edge = this_edge->next; + x = this_edge->pos.x; + y = this_edge->pos.y; + c_draw(window, x, y); + } + while (edgept != this_edge); +} + + +/********************************************************************** + * render_outline + * + * Create a list of line segments that represent the expanded outline + * that was supplied as input. + **********************************************************************/ +void render_outline(void *window, + TESSLINE *outline, + TPOINT origin, + C_COL color) { + /* No outline */ + if (!outline) + return; + /* Draw Compact outline */ + if (outline->loop) + render_edgepts (window, outline->loop, color); + /* Add on next outlines */ + render_outline (window, outline->next, origin, color); + + /* Add on child outlines */ + render_outline (window, outline->child, origin, Grey); +} diff --git a/wordrec/render.h b/wordrec/render.h new file mode 100644 index 0000000000..9853385012 --- /dev/null +++ b/wordrec/render.h @@ -0,0 +1,90 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: render.h (Formerly render.h) + * Description: Convert the various data type into line lists + * Author: Mark Seaman, OCR Technology + * Created: Fri Jul 28 13:14:48 1989 + * Modified: Fri Apr 26 09:59:45 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef RENDER_H +#define RENDER_H + +#include "host.h" +#include "callcpp.h" +#include "tessclas.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern void *blob_window; /* Window for blobs */ +extern C_COL color_list[]; /* Colors for outlines */ +extern int blob_pause; /* Wait after each blob */ +extern int display_all_blobs; /* Display blobs ? */ +extern int display_all_words; /* Display words ? */ + +#define NUM_COLORS 6 + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void display_blob(TBLOB *blob, C_COL color); + +void init_render_vars(); + +void render_blob(void *window, TBLOB *blob, TPOINT origin, C_COL color); + +void render_edgepts(void *window, EDGEPT *edgept, C_COL color); + +void render_outline(void *window, + TESSLINE *outline, + TPOINT origin, + C_COL color); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* render.c +void display_blob + _ARGS((BLOB *blob, + char *color)); + +void render_blob + _ARGS((X_WINDOW *window, + BLOB *blob, + TPOINT origin, + char *color)); + +void render_edgepts + _ARGS((X_WINDOW *window, + EDGEPT *edgept, + char *color)); + +void render_outline + _ARGS((X_WINDOW *window, + TESSLINE *outline, + TPOINT origin, + char *color)); + +#undef _ARGS +*/ +#endif diff --git a/wordrec/seam.cpp b/wordrec/seam.cpp new file mode 100644 index 0000000000..24d5c036bb --- /dev/null +++ b/wordrec/seam.cpp @@ -0,0 +1,482 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: seam.c (Formerly seam.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Fri May 17 16:30:13 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "seam.h" +#include "callcpp.h" +#include "structures.h" +#include "makechop.h" + +#ifdef __UNIX__ +#include +#endif + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +#define NUM_STARTING_SEAMS 20 + +#define SEAMBLOCK 100 /* Cells per block */ +makestructure (newseam, free_seam, printseam, SEAM, +freeseam, SEAMBLOCK, "SEAM", seamcount); + +/*---------------------------------------------------------------------- + Public Function Code +----------------------------------------------------------------------*/ +/********************************************************************** + * point_in_split + * + * Check to see if either of these points are present in the current + * split. Return TRUE if one of them is. + **********************************************************************/ +bool point_in_split(SPLIT *split, EDGEPT *point1, EDGEPT *point2) { + return ((split) ? + ((exact_point (split->point1, point1) || + exact_point (split->point1, point2) || + exact_point (split->point2, point1) || + exact_point (split->point2, point2)) ? TRUE : FALSE) : FALSE); +} + + +/********************************************************************** + * point_in_seam + * + * Check to see if either of these points are present in the current + * seam. Return TRUE if one of them is. + **********************************************************************/ +bool point_in_seam(SEAM *seam, SPLIT *split) { + return (point_in_split (seam->split1, split->point1, split->point2) || + point_in_split (seam->split2, split->point1, split->point2) || + point_in_split (seam->split3, split->point1, split->point2)); +} + + +/********************************************************************** + * add_seam + * + * Add another seam to a collection of seams. + **********************************************************************/ +SEAMS add_seam(SEAMS seam_list, SEAM *seam) { + return (array_push (seam_list, seam)); +} + + +/********************************************************************** + * combine_seam + * + * Combine two seam records into a single seam. Move the split + * references from the second seam to the first one. The argument + * convention is patterned after strcpy. + **********************************************************************/ +void combine_seams(SEAM *dest_seam, SEAM *source_seam) { + dest_seam->priority += source_seam->priority; + dest_seam->location += source_seam->location; + dest_seam->location /= 2; + + if (source_seam->split1) { + if (!dest_seam->split1) + dest_seam->split1 = source_seam->split1; + else if (!dest_seam->split2) + dest_seam->split2 = source_seam->split1; + else if (!dest_seam->split3) + dest_seam->split3 = source_seam->split1; + else + cprintf ("combine_seam: Seam is too crowded, can't be combined !\n"); + } + if (source_seam->split2) { + if (!dest_seam->split2) + dest_seam->split2 = source_seam->split2; + else if (!dest_seam->split3) + dest_seam->split3 = source_seam->split2; + else + cprintf ("combine_seam: Seam is too crowded, can't be combined !\n"); + } + if (source_seam->split3) { + if (!dest_seam->split3) + dest_seam->split3 = source_seam->split3; + else + cprintf ("combine_seam: Seam is too crowded, can't be combined !\n"); + } + free_seam(source_seam); +} + + +/********************************************************************** + * delete_seam + * + * Free this seam record and the splits that are attached to it. + **********************************************************************/ +void delete_seam(void *arg) { //SEAM *seam) + SEAM *seam = (SEAM *) arg; + + if (seam) { + if (seam->split1) + delete_split (seam->split1); + if (seam->split2) + delete_split (seam->split2); + if (seam->split3) + delete_split (seam->split3); + free_seam(seam); + } +} + + +/********************************************************************** + * free_seam_list + * + * Free all the seams that have been allocated in this list. Reclaim + * the memory for each of the splits as well. + **********************************************************************/ +void free_seam_list(SEAMS seam_list) { + int x; + + array_loop (seam_list, x) delete_seam (array_value (seam_list, x)); + array_free(seam_list); +} + + +/********************************************************************** + * test_insert_seam + * + * Return true if insert_seam will succeed. + **********************************************************************/ +bool test_insert_seam(SEAMS seam_list, + int index, + TBLOB *left_blob, + TBLOB *first_blob) { + SEAM *test_seam; + TBLOB *blob; + int test_index; + int list_length; + + list_length = array_count (seam_list); + for (test_index = 0, blob = first_blob->next; + test_index < index; test_index++, blob = blob->next) { + test_seam = (SEAM *) array_value (seam_list, test_index); + if (test_index + test_seam->widthp < index && + test_seam->widthp + test_index == index - 1 && + account_splits_right(test_seam, blob) < 0) + return false; + } + for (test_index = index, blob = left_blob->next; + test_index < list_length; test_index++, blob = blob->next) { + test_seam = (SEAM *) array_value (seam_list, test_index); + if (test_index - test_seam->widthn >= index && + test_index - test_seam->widthn == index && + account_splits_left(test_seam, first_blob, blob) < 0) + return false; + } + return true; +} + +/********************************************************************** + * insert_seam + * + * Add another seam to a collection of seams at a particular location + * in the seam array. + **********************************************************************/ +SEAMS insert_seam(SEAMS seam_list, + int index, + SEAM *seam, + TBLOB *left_blob, + TBLOB *first_blob) { + SEAM *test_seam; + TBLOB *blob; + int test_index; + int list_length; + + list_length = array_count (seam_list); + for (test_index = 0, blob = first_blob->next; + test_index < index; test_index++, blob = blob->next) { + test_seam = (SEAM *) array_value (seam_list, test_index); + if (test_index + test_seam->widthp >= index) { + test_seam->widthp++; /*got in the way */ + } + else if (test_seam->widthp + test_index == index - 1) { + test_seam->widthp = account_splits_right(test_seam, blob); + if (test_seam->widthp < 0) { + cprintf ("Failed to find any right blob for a split!\n"); + print_seam("New dud seam", seam); + print_seam("Failed seam", test_seam); + } + } + } + for (test_index = index, blob = left_blob->next; + test_index < list_length; test_index++, blob = blob->next) { + test_seam = (SEAM *) array_value (seam_list, test_index); + if (test_index - test_seam->widthn < index) { + test_seam->widthn++; /*got in the way */ + } + else if (test_index - test_seam->widthn == index) { + test_seam->widthn = account_splits_left(test_seam, first_blob, blob); + if (test_seam->widthn < 0) { + cprintf ("Failed to find any left blob for a split!\n"); + print_seam("New dud seam", seam); + print_seam("Failed seam", test_seam); + } + } + } + return (array_insert (seam_list, index, seam)); +} + + +/********************************************************************** + * account_splits_right + * + * Account for all the splits by looking to the right. + * in the blob list. + **********************************************************************/ +int account_splits_right(SEAM *seam, TBLOB *blob) { + INT8 found_em[3]; + INT8 width; + + found_em[0] = seam->split1 == NULL; + found_em[1] = seam->split2 == NULL; + found_em[2] = seam->split3 == NULL; + if (found_em[0] && found_em[1] && found_em[2]) + return 0; + width = 0; + do { + if (!found_em[0]) + found_em[0] = find_split_in_blob (seam->split1, blob); + if (!found_em[1]) + found_em[1] = find_split_in_blob (seam->split2, blob); + if (!found_em[2]) + found_em[2] = find_split_in_blob (seam->split3, blob); + if (found_em[0] && found_em[1] && found_em[2]) { + return width; + } + width++; + blob = blob->next; + } + while (blob != NULL); + return -1; +} + + +/********************************************************************** + * account_splits_left + * + * Account for all the splits by looking to the left. + * in the blob list. + **********************************************************************/ +int account_splits_left(SEAM *seam, TBLOB *blob, TBLOB *end_blob) { + static INT32 depth = 0; + static INT8 width; + static INT8 found_em[3]; + + if (blob != end_blob) { + depth++; + account_splits_left (seam, blob->next, end_blob); + depth--; + } + else { + found_em[0] = seam->split1 == NULL; + found_em[1] = seam->split2 == NULL; + found_em[2] = seam->split3 == NULL; + width = 0; + } + if (!found_em[0]) + found_em[0] = find_split_in_blob (seam->split1, blob); + if (!found_em[1]) + found_em[1] = find_split_in_blob (seam->split2, blob); + if (!found_em[2]) + found_em[2] = find_split_in_blob (seam->split3, blob); + if (!found_em[0] || !found_em[1] || !found_em[2]) { + width++; + if (depth == 0) { + width = -1; + } + } + return width; +} + + +/********************************************************************** + * find_split_in_blob + * + * Return TRUE if the split is somewhere in this blob. + **********************************************************************/ +bool find_split_in_blob(SPLIT *split, TBLOB *blob) { + TESSLINE *outline; + +#if 0 + for (outline = blob->outlines; outline != NULL; outline = outline->next) + if (is_split_outline (outline, split)) + return TRUE; + return FALSE; +#endif + for (outline = blob->outlines; outline != NULL; outline = outline->next) + if (point_in_outline(split->point1, outline)) + break; + if (outline == NULL) + return FALSE; + for (outline = blob->outlines; outline != NULL; outline = outline->next) + if (point_in_outline(split->point2, outline)) + return TRUE; + return FALSE; +} + + +/********************************************************************** + * join_two_seams + * + * Merge these two seams into a new seam. Duplicate the split records + * in both of the input seams. Return the resultant seam. + **********************************************************************/ +SEAM *join_two_seams(SEAM *seam1, SEAM *seam2) { + SEAM *result = NULL; + SEAM *temp; + + assert(seam1 &&seam2); + + if ((seam1->split3 == NULL && seam2->split2 == NULL || + seam1->split2 == NULL && seam2->split3 == NULL || + seam1->split1 == NULL || + seam2->split1 == NULL) && (!shared_split_points (seam1, seam2))) { + clone_seam(result, seam1); + clone_seam(temp, seam2); + combine_seams(result, temp); + } + return (result); +} + + +/********************************************************************** + * new_seam + * + * Create a structure for a "seam" between two blobs. This data + * structure may actually hold up to three different splits. + * Initailization of this record is done by this routine. + **********************************************************************/ +SEAM *new_seam(PRIORITY priority, + int x_location, + SPLIT *split1, + SPLIT *split2, + SPLIT *split3) { + SEAM *seam; + + seam = newseam (); + + seam->priority = priority; + seam->location = x_location; + seam->widthp = 0; + seam->widthn = 0; + seam->split1 = split1; + seam->split2 = split2; + seam->split3 = split3; + + return (seam); +} + + +/********************************************************************** + * new_seam_list + * + * Create a collection of seam records in an array. + **********************************************************************/ +SEAMS new_seam_list() { + return (array_new (NUM_STARTING_SEAMS)); +} + + +/********************************************************************** + * print_seam + * + * Print a list of splits. Show the coordinates of both points in + * each split. + **********************************************************************/ +void print_seam(const char *label, SEAM *seam) { + if (seam) { + cprintf(label); + cprintf (" %6.2f @ %5d, p=%d, n=%d ", + seam->priority, seam->location, seam->widthp, seam->widthn); + + print_split (seam->split1); + + if (seam->split2) { + cprintf (", "); + print_split (seam->split2); + + if (seam->split3) { + cprintf (", "); + print_split (seam->split3); + } + } + cprintf ("\n"); + } +} + + +/********************************************************************** + * print_seams + * + * Print a list of splits. Show the coordinates of both points in + * each split. + **********************************************************************/ +void print_seams(const char *label, SEAMS seams) { + int x; + char number[CHARS_PER_LINE]; + + if (seams) { + cprintf ("%s\n", label); + array_loop(seams, x) { + sprintf (number, "%2d: ", x); + print_seam (number, (SEAM *) array_value (seams, x)); + } + cprintf ("\n"); + } +} + + +/********************************************************************** + * shared_split_points + * + * Check these two seams to make sure that neither of them have two + * points in common. Return TRUE if any of the same points are present + * in any of the splits of both seams. + **********************************************************************/ +int shared_split_points(SEAM *seam1, SEAM *seam2) { + if (seam1 == NULL || seam2 == NULL) + return (FALSE); + + if (seam2->split1 == NULL) + return (FALSE); + if (point_in_seam (seam1, seam2->split1)) + return (TRUE); + + if (seam2->split2 == NULL) + return (FALSE); + if (point_in_seam (seam1, seam2->split2)) + return (TRUE); + + if (seam2->split3 == NULL) + return (FALSE); + if (point_in_seam (seam1, seam2->split3)) + return (TRUE); + + return (FALSE); +} diff --git a/wordrec/seam.h b/wordrec/seam.h new file mode 100644 index 0000000000..0006b0b51e --- /dev/null +++ b/wordrec/seam.h @@ -0,0 +1,136 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: seam.h (Formerly seam.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Thu May 16 17:05:52 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef SEAM_H +#define SEAM_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "split.h" +#include "tessarray.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef float PRIORITY; /* PRIORITY */ + +typedef struct seam_record +{ /* SEAM */ + PRIORITY priority; + INT8 widthp; + INT8 widthn; + INT16 location; + SPLIT *split1; + SPLIT *split2; + SPLIT *split3; +} SEAM; + +typedef ARRAY SEAMS; /* SEAMS */ + +extern SEAM *newseam(); + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * clone_seam + * + * Create a new seam record and copy the contents of this seam into it. + **********************************************************************/ + +#define clone_seam(dest,source) \ +if (source) { \ + (dest) = newseam (); \ + (dest)->location = (source)->location; \ + (dest)->widthp = (source)->widthp; \ + (dest)->widthn = (source)->widthn; \ + (dest)->priority = (source)->priority; \ + clone_split ((dest)->split1, (source)->split1); \ + clone_split ((dest)->split2, (source)->split2); \ + clone_split ((dest)->split3, (source)->split3); \ +} \ +else { \ + (dest) = (SEAM*) NULL; \ +} \ + + +/********************************************************************** + * exact_point + * + * Return TRUE if the point positions are the exactly the same. The + * parameters must be of type (EDGEPT*). + **********************************************************************/ + +#define exact_point(p1,p2) \ + (! ((p1->pos.x - p2->pos.x) || (p1->pos.y - p2->pos.y))) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +bool point_in_split(SPLIT *split, EDGEPT *point1, EDGEPT *point2); + +bool point_in_seam(SEAM *seam, SPLIT *split); + +SEAMS add_seam(SEAMS seam_list, SEAM *seam); + +void combine_seams(SEAM *dest_seam, SEAM *source_seam); + +void delete_seam(void *arg); //SEAM *seam); + +void free_seam_list(SEAMS seam_list); + +bool test_insert_seam(SEAMS seam_list, + int index, + TBLOB *left_blob, + TBLOB *first_blob); + +SEAMS insert_seam(SEAMS seam_list, + int index, + SEAM *seam, + TBLOB *left_blob, + TBLOB *first_blob); + +int account_splits_right(SEAM *seam, TBLOB *blob); + +int account_splits_left(SEAM *seam, TBLOB *blob, TBLOB *end_blob); + +bool find_split_in_blob(SPLIT *split, TBLOB *blob); + +SEAM *join_two_seams(SEAM *seam1, SEAM *seam2); + +SEAM *new_seam(PRIORITY priority, + int x_location, + SPLIT *split1, + SPLIT *split2, + SPLIT *split3); + +SEAMS new_seam_list(); + +void print_seam(const char *label, SEAM *seam); + +void print_seams(const char *label, SEAMS seams); + +int shared_split_points(SEAM *seam1, SEAM *seam2); +#endif diff --git a/wordrec/split.cpp b/wordrec/split.cpp new file mode 100644 index 0000000000..f2ad47d151 --- /dev/null +++ b/wordrec/split.cpp @@ -0,0 +1,182 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: split.c (Formerly split.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Fri May 17 16:27:49 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "split.h" +#include "structures.h" +#include "debug.h" +#include "hideedge.h" +#include "callcpp.h" + +#ifdef __UNIX__ +#include +#endif + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +make_toggle_var (display_splits, 0, make_display_splits, +3, 3, set_display_splits, "Display splits"); + +#define SPLITBLOCK 100 /* Cells per block */ +makestructure (newsplit, free_split, printsplit, SPLIT, +freesplit, SPLITBLOCK, "SPLIT", splitcount); + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * init_splitter_vars + * + * Create and initialize references to debug variables that control + * operations in this file. + **********************************************************************/ +void init_splitter_vars() { + make_display_splits(); +} + + +/********************************************************************** + * delete_split + * + * Remove this split from existance. Take if off the display list and + * deallocate its memory. + **********************************************************************/ +void delete_split(SPLIT *split) { + if (split) { + free_split(split); + } +} + + +/********************************************************************** + * make_edgept + * + * Create an EDGEPT and hook it into an existing list of edge points. + **********************************************************************/ +EDGEPT *make_edgept(int x, int y, EDGEPT *next, EDGEPT *prev) { + EDGEPT *this_edgept; + /* Create point */ + this_edgept = newedgept (); + this_edgept->pos.x = x; + this_edgept->pos.y = y; + /* Hook it up */ + this_edgept->next = next; + this_edgept->prev = prev; + prev->next = this_edgept; + next->prev = this_edgept; + /* Set up vec entries */ + this_edgept->vec.x = this_edgept->next->pos.x - x; + this_edgept->vec.y = this_edgept->next->pos.y - y; + this_edgept->prev->vec.x = x - this_edgept->prev->pos.x; + this_edgept->prev->vec.y = y - this_edgept->prev->pos.y; + + reveal_edge(this_edgept); + this_edgept->flags[1] = 0; + + return (this_edgept); +} + + +/********************************************************************** + * new_split + * + * Create a new split record and initialize it. Put it on the display + * list. + **********************************************************************/ +SPLIT *new_split(EDGEPT *point1, EDGEPT *point2) { + SPLIT *s; + s = (SPLIT *) newsplit (); + s->point1 = point1; + s->point2 = point2; + return (s); +} + + +/********************************************************************** + * print_split + * + * Print a list of splits. Show the coordinates of both points in + * each split. + **********************************************************************/ +void print_split(SPLIT *split) { + if (split) { + cprintf ("(%d,%d)--(%d,%d)", + split->point1->pos.x, split->point1->pos.y, + split->point2->pos.x, split->point2->pos.y); + } +} + + +/********************************************************************** + * split_outline + * + * Split between these two edge points. Apply a split and return a + * pointer to the other side of the split. + **********************************************************************/ +void split_outline(EDGEPT *join_point1, EDGEPT *join_point2) { + EDGEPT *join_point1a; + EDGEPT *temp2; + EDGEPT *temp1; + + assert (join_point1 != join_point2); + + temp2 = join_point2->next; + temp1 = join_point1->next; + /* Create two new points */ + join_point1a = make_edgept (join_point1->pos.x, + join_point1->pos.y, temp1, join_point2); + + make_edgept (join_point2->pos.x, join_point2->pos.y, temp2, join_point1); +} + + +/********************************************************************** + * unsplit_outlines + * + * Remove the split that was put between these two points. + **********************************************************************/ +void unsplit_outlines(EDGEPT *p1, EDGEPT *p2) { + EDGEPT *tmp1 = p1->next; + EDGEPT *tmp2 = p2->next; + + assert (p1 != p2); + + tmp1->next->prev = p2; + tmp2->next->prev = p1; + + p1->next = tmp2->next; + p2->next = tmp1->next; + + oldedgept(tmp1); + oldedgept(tmp2); + + p1->vec.x = p1->next->pos.x - p1->pos.x; + p1->vec.y = p1->next->pos.y - p1->pos.y; + + p2->vec.x = p2->next->pos.x - p2->pos.x; + p2->vec.y = p2->next->pos.y - p2->pos.y; +} diff --git a/wordrec/split.h b/wordrec/split.h new file mode 100644 index 0000000000..7d6f278076 --- /dev/null +++ b/wordrec/split.h @@ -0,0 +1,115 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: split.h (Formerly split.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon May 13 10:49:23 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *****************************************************************************/ +#ifndef SPLIT_H +#define SPLIT_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "tessclas.h" +#include "oldlist.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef struct split_record +{ /* SPLIT */ + EDGEPT *point1; + EDGEPT *point2; +} SPLIT; + +typedef LIST SPLITS; /* SPLITS */ + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int display_splits; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * clone_split + * + * Create a new split record and set the contents equal to the contents + * of this record. + **********************************************************************/ + +#define clone_split(dest,source) \ +if (source) \ + (dest) = new_split ((source)->point1, (source)->point2); \ +else \ + (dest) = (SPLIT*) NULL \ + + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void init_splitter_vars(); + +void delete_split(SPLIT *split); + +EDGEPT *make_edgept(int x, int y, EDGEPT *next, EDGEPT *prev); + +SPLIT *new_split(EDGEPT *point1, EDGEPT *point2); + +void print_split(SPLIT *split); + +void split_outline(EDGEPT *join_point1, EDGEPT *join_point2); + +void unsplit_outlines(EDGEPT *p1, EDGEPT *p2); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* split.c +void init_splitter_vars + _ARGS((void)); + +void delete_split + _ARGS((SPLIT *split)); + +SPLIT *new_split + _ARGS((EDGEPT *point1, + EDGEPT *point2)); + +void print_split + _ARGS((SPLIT *split)); + +void split_outline + _ARGS((EDGEPT *join_point1, + EDGEPT *join_point2)); + +void unsplit_outlines + _ARGS((EDGEPT *p1, + EDGEPT *p2)); + +#undef _ARGS +*/ +#endif diff --git a/wordrec/tally.cpp b/wordrec/tally.cpp new file mode 100644 index 0000000000..ca9126751a --- /dev/null +++ b/wordrec/tally.cpp @@ -0,0 +1,68 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: tally.c (Formerly tally.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon Apr 8 11:41:32 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "tally.h" +#include "freelist.h" +#include "callcpp.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * new_tally + * + * Create a new tally record and initialize it. + **********************************************************************/ +TALLY new_tally(int num_buckets) { + TALLY t; + int x; + + t = (TALLY) memalloc ((num_buckets + 2) * sizeof (int)); + t->count = 0; + t->num_buckets = num_buckets; + + iterate_tally (t, x) tally_entry (t, x) = 0; + + return (t); +} + + +/********************************************************************** + * print_tally + * + * Print the results of a given tally. + **********************************************************************/ +void print_tally(FILE *file, const char *string, TALLY t) { + int x; + + cprintf ("%d %s buckets\n", t->num_buckets, string); + cprintf ("%d samples in %s\n", t->count, string); + + iterate_tally (t, x) + cprintf (" %s [%d] = %d\n", string, x, tally_entry (t, x)); + cprintf ("\n"); +} diff --git a/wordrec/tally.h b/wordrec/tally.h new file mode 100644 index 0000000000..4d650fde38 --- /dev/null +++ b/wordrec/tally.h @@ -0,0 +1,94 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: tally.h (Formerly tally.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Wed Apr 10 10:45:41 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef TALLY_H +#define TALLY_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef struct _TALLY_ +{ + int count; + int num_buckets; + int buckets[1]; +} *TALLY; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * inc_tally_bucket + * + * Increment the bucket count for the chosen bucket. + **********************************************************************/ + +#define inc_tally_bucket(t,i) \ +(t->count++, \ + ((i < t->num_buckets) ? \ + (t->buckets[i]++) : \ + (t->buckets[t->num_buckets-1]++))) + +/********************************************************************** + * iterate_tally + * + * Iterate through all the buckets in a tally record. + **********************************************************************/ + +#define iterate_tally(t,i) \ +for (i=0; inum_buckets; i++) + +/********************************************************************** + * tally_entry + * + * Access one of the buckets of a tally record without bounds checking. + **********************************************************************/ + +#define tally_entry(t,i) \ +(t->buckets[i]) + +/********************************************************************** + * tally_value + * + * Access one of the buckets of a tally record with bounds checking. + ********************************************************************** + +#define tally_value(t,i) \ +((i>=0 && inum_buckets) ? \ + (tally_entry (t,i)) : \ + (cprintf ("error: tried to access non-existant bucket %d\n", i))) +*/ + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +TALLY new_tally(int num_buckets); + +void print_tally(FILE *file, const char *string, TALLY t); +#endif diff --git a/wordrec/tessinit.cpp b/wordrec/tessinit.cpp new file mode 100644 index 0000000000..d4af7c4299 --- /dev/null +++ b/wordrec/tessinit.cpp @@ -0,0 +1,108 @@ +/********************************************************************** + * File: tessinit.c (Formerly tessinit.c) + * Description: Stuff from recog.c needed by tessedit. + * Author: Ray Smith + * Created: Thu Jan 23 09:33:59 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#include "globals.h" +#include "variables.h" /* Feature stuff */ + +#include +#include +#include "tessinit.h" + +/*---------------------------------------------------------------------- + Variables +----------------------------------------------------------------------*/ +int corner_0; +int corner_1; +int corner_2; +int corner_3; + +int row_number; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * program_variables + * + * Initialize all the things in the program that need to be initialized. + **********************************************************************/ +void program_variables() { + VALUE dummy; + + int_variable (plots_fx, "plots_fx", 0); + int_variable (plots_ocr, "plots_ocr", 0); + int_variable (debugs_fx, "debugs_fx", 0); + int_variable (debugs_ocr, "debugs_ocr", 0); + /* PREV DEFAULT 0 */ + int_variable (acts_fx, "acts_fx", 2048); + /* PREV DEFAULT 0 */ + int_variable (acts_ocr, "acts_ocr", 32); + int_variable (corner_0, "corner_0", 0); + int_variable (corner_1, "corner_1", 0); + int_variable (corner_2, "corner_2", 2550); + int_variable (corner_3, "corner_3", 3508); + int_variable (resolution, "resolution", 300); + string_variable (debugfile, "debugfile", ""); +} + + +/********************************************************************** + * program_init + * + * Initialize all the things in the program that need to be initialized. + **********************************************************************/ +void program_init() { + /* Plots flags */ + plots[OCR] = plots_ocr; + debugs[OCR] = debugs_ocr; + acts[OCR] = acts_ocr; + plots[FX] = plots_fx; + debugs[FX] = debugs_fx; + acts[FX] = acts_fx; + + corners[0] = corner_0; + corners[1] = corner_1; + corners[2] = corner_2; + corners[3] = corner_3; +} + + +/********************************************************************** + * matherr + * + * Trap procedure for the Standard Math library + **********************************************************************/ +#ifdef __UNIX +int +matherr (error) +struct exception *error; +{ + if (error->type == DOMAIN) { + if (!strcmp (error->name, "sqrt")) { + cprintf ("Sqrt:Domain error!!\n"); + abort(); + } + if (!strcmp (error->name, "atan2")) { + cprintf ("Arc Tangent error: atan2 (%ld, %ld)\n", + error->arg1, error->arg2); + abort(); + } + } + return 0; /*all others default */ +} +#endif diff --git a/wordrec/tessinit.h b/wordrec/tessinit.h new file mode 100644 index 0000000000..28e1d37497 --- /dev/null +++ b/wordrec/tessinit.h @@ -0,0 +1,46 @@ +/********************************************************************** + * File: tessinit.h (Formerly tessinit.h) + * Description: Stuff from recog needed by tessedit. + * Author: Ray Smith + * Created: Thu Jan 23 09:36:25 GMT 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#ifndef TESSINIT_H +#define TESSINIT_H + +/*--------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------*/ +void program_variables(); + +void program_init(); + +void program_init _ARGS ((void)); + +void program_variables _ARGS ((void)); + +#undef _ARGS + +//extern int imagescale; +//extern int edgescale; + +extern int corner_0; +extern int corner_1; +extern int corner_2; +extern int corner_3; + +extern int row_number; + +extern FILE *boxfp; +#endif diff --git a/wordrec/tface.cpp b/wordrec/tface.cpp new file mode 100644 index 0000000000..ecc9dd4061 --- /dev/null +++ b/wordrec/tface.cpp @@ -0,0 +1,273 @@ +/********************************************************************** + * File: tface.c (Formerly tface.c) + * Description: C side of the Tess/tessedit C/C++ interface. + * Author: Ray Smith + * Created: Mon Apr 27 11:57:06 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#include "tface.h" +#include "danerror.h" +#include "globals.h" +#include "tordvars.h" /* Feature stuff */ +#include "fxid.h" +#include "wordclass.h" +#include "bestfirst.h" +#include "context.h" +#include "gradechop.h" +#include "hyphen.h" +/* includes for init */ +#include "msmenus.h" +#include "djmenus.h" +#include "tessinit.h" +#include "mfvars.h" +#include "variables.h" +#include "metrics.h" +#include "adaptmatch.h" +#include "matchtab.h" +#include "chopper.h" +#include "permdawg.h" +#include "permute.h" +#include "chop.h" +#include "callcpp.h" +#include "badwords.h" + +#include +#ifdef __UNIX__ +#include +#endif +//extern "C" int record_matcher_output; + +/*---------------------------------------------------------------------- + Variables +----------------------------------------------------------------------*/ +static PRIORITY pass2_ok_split; +static int pass2_seg_states; +extern int NO_BLOCK; +/*---------------------------------------------------------------------- + Function Code +----------------------------------------------------------------------*/ +/********************************************************************** + * start_recog + * + * Startup recog program ready to recognize words. + **********************************************************************/ +int start_recog(const char *configfile, const char *textbase) { + + program_editup(configfile); + program_editup2(textbase); + return (0); +} + + +/********************************************************************** + * program_editup + * + * Initialize all the things in the program that need to be initialized. + **********************************************************************/ +void program_editup(const char *configfile) { + init_ms_debug(); + init_dj_debug(); + + program_variables(); + mfeature_variables(); + + if (configfile != NULL) { + // cprintf ("Reading configuration from file '%s'\n", configfile); + /* Read config file */ + read_variables(configfile); + } + /* Initialize subsystems */ + program_init(); + mfeature_init(); + setup_cp_maps(); +} + + +/*-------------------------------------------------------------------------*/ +void program_editup2(const char *textbase) { + if (textbase != NULL) { + strcpy(imagefile, textbase); + /* Read in data files */ + edit_with_ocr(textbase); + } + + init_metrics(); + pass2_ok_split = ok_split; + pass2_seg_states = num_seg_states; + reset_width_tally(); +} + + +/********************************************************************** + * edit_with_ocr + * + * Initialize all the things in the program needed before the classifier + * code is called. + **********************************************************************/ +void edit_with_ocr(const char *imagename) { + char name[FILENAMESIZE]; /*base name of file */ + + if (write_output) { + strcpy(name, imagename); + strcat (name, ".txt"); + //xiaofan + textfile = open_file (name, "w"); + } + if (write_raw_output) { + strcpy(name, imagename); + strcat (name, ".raw"); + rawfile = open_file (name, "w"); + } + if (record_matcher_output) { + strcpy(name, imagename); + strcat (name, ".mlg"); + matcher_fp = open_file (name, "w"); + strcpy(name, imagename); + strcat (name, ".ctx"); + correct_fp = open_file (name, "r"); + } +} + + +/********************************************************************** + * end_recog + * + * Cleanup and exit the recog program. + **********************************************************************/ +int end_recog() { + program_editdown (0); + + return (0); +} + + +/********************************************************************** + * program_editdown + * + * This function holds any nessessary post processing for the Wise Owl + * program. + **********************************************************************/ +void program_editdown(INT32 elasped_time) { + dj_cleanup(); + if (display_text) + cprintf ("\n"); + if (!NO_BLOCK && write_output) + fprintf (textfile, "\n"); + if (write_raw_output) + fprintf (rawfile, "\n"); + if (write_output) { + #ifdef __UNIX__ + fsync (fileno (textfile)); + #endif + fclose(textfile); + } + if (write_raw_output) { + #ifdef __UNIX__ + fsync (fileno (rawfile)); + #endif + fclose(rawfile); + } + close_choices(); + if (tessedit_save_stats) + save_summary (elasped_time); + end_match_table(); + InitChoiceAccum(); + if (global_hash != NULL) { + free_mem(global_hash); + global_hash = NULL; + } + end_metrics(); + end_permute(); + end_permdawg(); + free_variables(); +} + + +/********************************************************************** + * set_pass1 + * + * Get ready to do some pass 1 stuff. + **********************************************************************/ +void set_pass1() { + blob_skip = FALSE; + ok_split = 70.0; + num_seg_states = 15; + SettupPass1(); + first_pass = 1; +} + + +/********************************************************************** + * set_pass2 + * + * Get ready to do some pass 2 stuff. + **********************************************************************/ +void set_pass2() { + blob_skip = FALSE; + ok_split = pass2_ok_split; + num_seg_states = pass2_seg_states; + SettupPass2(); + first_pass = 0; +} + + +/********************************************************************** + * cc_recog + * + * Recognize a word. + **********************************************************************/ +CHOICES_LIST cc_recog(TWERD *tessword, + A_CHOICE *best_choice, + A_CHOICE *best_raw_choice, + BOOL8 tester, + BOOL8 trainer) { + int fx; + CHOICES_LIST results; /*matcher results */ + + if (SetErrorTrap (NULL)) { + cprintf ("Tess copped out!\n"); + ReleaseErrorTrap(); + class_string (best_choice) = NULL; + return NULL; + } + InitChoiceAccum(); + init_match_table(); + for (fx = 0; fx < MAX_FX && (acts[OCR] & (FXSELECT << fx)) == 0; fx++); + results = + chop_word_main(tessword, + fx, + best_choice, + best_raw_choice, + tester, + trainer); + DebugWordChoices(); + reset_hyphen_word(); + ReleaseErrorTrap(); + return results; +} + + +/********************************************************************** + * dict_word() + * + * Test the dictionaries, returning NO_PERM (0) if not found, or one of the + * DAWG_PERM values if found, according to the dictionary. + **********************************************************************/ +int dict_word(const char *word) { + + if (test_freq_words (word)) + return FREQ_DAWG_PERM; + else + return valid_word (word); +} diff --git a/wordrec/tface.h b/wordrec/tface.h new file mode 100644 index 0000000000..79919bdb89 --- /dev/null +++ b/wordrec/tface.h @@ -0,0 +1,35 @@ +#ifndef TFACE_H +#define TFACE_H + +#include "host.h" +#include "choicearr.h" +#include "tessclas.h" +#include "cutil.h" + +/*---------------------------------------------------------------------------- + Function Prototypes +----------------------------------------------------------------------------*/ +int start_recog(const char *configfile, const char *imagebase); + +void program_editup(const char *configfile); + +void program_editup2(const char *imagebase); + +void edit_with_ocr(const char *imagename); + +int end_recog(); + +void program_editdown(INT32 elasped_time); + +void set_pass1(); + +void set_pass2(); + +CHOICES_LIST cc_recog(TWERD *tessword, + A_CHOICE *best_choice, + A_CHOICE *best_raw_choice, + BOOL8 tester, + BOOL8 trainer); + +int dict_word(const char *word); +#endif diff --git a/wordrec/wordclass.cpp b/wordrec/wordclass.cpp new file mode 100644 index 0000000000..aad9d9d367 --- /dev/null +++ b/wordrec/wordclass.cpp @@ -0,0 +1,277 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: wordclass.c (Formerly wordclass.c) + * Description: Word classifier + * Author: Mark Seaman, OCR Technology + * Created: Tue Jan 30 14:03:25 1990 + * Modified: Fri Jul 12 16:03:06 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I N C L U D E S +----------------------------------------------------------------------*/ +#include +#ifdef __UNIX__ +#include +#endif + +#include "wordclass.h" +#include "fxid.h" +#include "tordvars.h" +#include "associate.h" +#include "render.h" +#include "metrics.h" +#include "matchtab.h" +//#include "tfacepp.h" +#include "permute.h" +#include "context.h" +#include "badwords.h" +#include "callcpp.h" + +extern TBLOB *newblob(); + +/*---------------------------------------------------------------------- + Variables +----------------------------------------------------------------------*/ +INT16 first_pass; + +/*---------------------------------------------------------------------- + C o n s t a n t s +----------------------------------------------------------------------*/ + +#define BOLD_ON "&dB(s3B" +#define BOLD_OFF "&d@(s0B" +#define UNDERLINE_ON "&dD" +#define UNDERLINE_OFF "&d@" +LIST call_matcher( //call a matcher + TBLOB *ptblob, //previous + TBLOB *tessblob, //blob to match + TBLOB *ntblob, //next + void *, //unused parameter + TEXTROW * //always null anyway + ); + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * classify_blob + * + * Classify the this blob if it is not already recorded in the match + * table. Attempt to recognize this blob as a character. The recognition + * rating (probability) for this blob will be stored as a part of the + * blob. This value will also be returned to the caller. + **********************************************************************/ +CHOICES classify_blob(TBLOB *pblob, + TBLOB *blob, + TBLOB *nblob, + TEXTROW *row, + int fx, + const char *string, + C_COL color, + STATE *this_state, + STATE *best_state, + INT32 pass, + INT32 blob_index) { + CHOICES rating; + INT32 old_index; + + chars_classified++; /* Global value */ + if (blob_skip) + return (NIL); + +#ifndef GRAPHICS_DISABLED + if (display_all_blobs) + display_blob(blob, color); +#endif + rating = get_match (blob); + if (rating == NIL) { + if (pass) { + old_index = blob_index; + //?cast to int* + blob_type = compare_states (best_state, this_state, (int *) &blob_index); + blob_answer = word_answer[blob_index]; + if (blob_answer < '!') + fprintf (matcher_fp, + "Bad compare states: best state=0x%x%x, this=0x%x%x, bits=" + INT32FORMAT ", index=" INT32FORMAT ", outdex=" + INT32FORMAT ", word=%s\n", best_state->part1, + best_state->part2, this_state->part1, this_state->part2, + bits_in_states, old_index, blob_index, word_answer); + } + else + blob_type = 0; + rating = /*(*blob_matchers [fx]) */ (CHOICES) call_matcher (pblob, blob, + nblob, NULL, + row); + put_match(blob, rating); + } + +#ifndef GRAPHICS_DISABLED + if (display_ratings && string) + print_choices(string, rating); + + if (blob_pause) + window_wait(blob_window); +#endif + + return (rating); +} + + +/********************************************************************** + * write_text_files + * + * Write an answer to the output file that is the raw guess (without + * context) directly from the classifier. + **********************************************************************/ +void write_text_files(TWERD *word, + char *raw_choice, + int same_row, + int good_word, + int firstpass) { + int x; + /* Raw output */ + if (write_raw_output) { + if (same_row) + fprintf (rawfile, "\n"); + if (raw_choice && strlen (raw_choice)) { + fprintf (rawfile, "%s ", raw_choice); + fflush(rawfile); + } + } + /* Text file output */ + if (write_output) { + if (same_row) + fprintf (textfile, "\n"); + if (word->guess && strlen (word->guess)) { + for (x = 0; x < word->blanks; x++) + fprintf (textfile, " "); + if (!firstpass) + fprintf(textfile, BOLD_ON); + if (!good_word) + fprintf(textfile, UNDERLINE_ON); + fprintf (textfile, "%s", word->guess); + if (!good_word) + fprintf(textfile, UNDERLINE_OFF); + if (!firstpass) + fprintf(textfile, BOLD_OFF); + fflush(textfile); + } + } + /* Global counters */ + character_count += (word->guess ? strlen (word->guess) : 0); + word_count++; +} + + +/********************************************************************** + * save_answer + * + * Write an answer to the output file that is the raw guess (without + * context) directly from the classifier. + **********************************************************************/ +void save_answer(TWERD *word, + TEXTROW *row, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + int firstpass) { + static TEXTROW *last_row; + char raw_answer[CHARS_PER_LINE]; + int answer_already; + int good_answer; + char *string = NULL; + + if (best_choice) { + good_answer = AcceptableResult (best_choice, raw_choice); + string = class_string (best_choice); + } + else { + good_answer = FALSE; + } + + if (firstpass) { + /* First pass */ + if (string) { + /* Got answer */ + add_document_word(best_choice); + + word->guess = string; + fix_quotes (word->guess); + strcpy (raw_answer, word->guess); + + record_certainty (class_certainty (best_choice), 1); + + if (good_answer) { + record_certainty (class_certainty (best_choice), 2); + strcat (raw_answer, " "); + strcat (raw_answer, class_string (raw_choice)); + word->guess = strsave (raw_answer); + word->guess[strlen (string)] = 0; + if (string) { + strfree(string); + class_string (best_choice) = NULL; + } + } + else { + /* Not good enough */ + if (word->guess) + strfree (word->guess); + word->guess = NULL; + } + } + else { + word->guess = NULL; + raw_answer[0] = '\0'; + } + } + else { + /* Second pass */ + answer_already = (word->guess != NULL); + if (answer_already) { + write_text_files (word, + &word->guess[strlen (word->guess) + 1], + (row != last_row), TRUE, TRUE); + } + else { + /* Required second pass */ + if (string) { + if (!good_answer && tessedit_save_stats) { + SaveBadWord (string, class_certainty (best_choice)); + } + record_certainty (class_certainty (best_choice), 2); + word->guess = class_string (best_choice); + fix_quotes (word->guess); + write_text_files (word, class_string (raw_choice), + (row != last_row), good_answer, FALSE); + } + } + } + /* Word Display */ + if (display_text) { + if (row != last_row) + cprintf ("\n"); + if (word->guess && strlen (word->guess)) + cprintf ("%s ", word->guess); + else + cprintf ("%s ", raw_answer); + fflush(stdout); + } + + last_row = row; +} diff --git a/wordrec/wordclass.h b/wordrec/wordclass.h new file mode 100644 index 0000000000..736cff758c --- /dev/null +++ b/wordrec/wordclass.h @@ -0,0 +1,64 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: wordclass.h + * Description: Word level classifier + * Author: Mark Seaman, OCR Technology + * Created: Mon Feb 5 11:42:51 1990 + * Modified: Thu Apr 18 14:33:24 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef WERDCLASSH +#define WERDCLASSH + +#include "choices.h" +#include "callcpp.h" +#include "states.h" +#include "tessclas.h" + +/*---------------------------------------------------------------------- + Variables +----------------------------------------------------------------------*/ +extern INT16 first_pass; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +CHOICES classify_blob(TBLOB *pblob, + TBLOB *blob, + TBLOB *nblob, + TEXTROW *row, + int fx, + const char *string, + C_COL color, + STATE *this_state, + STATE *best_state, + INT32 pass, + INT32 blob_index); + +void write_text_files(TWERD *word, + char *raw_choice, + int same_row, + int good_word, + int firstpass); + +void save_answer(TWERD *word, + TEXTROW *row, + A_CHOICE *best_choice, + A_CHOICE *raw_choice, + int firstpass); +#endif