Tesseract Soucre 4.1.3 updated
This commit is contained in:
Родитель
8c3d2ee97a
Коммит
caff5f581c
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
BasedOnStyle: Google
|
||||
# Only merge empty functions.
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
# Do not allow short if statements.
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
# Enforce always the same pointer alignment.
|
||||
DerivePointerAlignment: false
|
||||
IndentPPDirectives: AfterHash
|
|
@ -0,0 +1,23 @@
|
|||
Before you submit an issue, please review [the guidelines for this repository](https://github.com/tesseract-ocr/tesseract/blob/master/CONTRIBUTING.md).
|
||||
|
||||
Please report an issue only for a BUG, not for asking questions.
|
||||
|
||||
Note that it will be much easier for us to fix the issue if a test case that
|
||||
reproduces the problem is provided. Ideally this test case should not have any
|
||||
external dependencies. Provide a copy of the image or link to files for the test case.
|
||||
|
||||
Please delete this text and fill in the template below.
|
||||
|
||||
------------------------
|
||||
|
||||
### Environment
|
||||
|
||||
* **Tesseract Version**: <!-- compulsory. you must provide your version -->
|
||||
* **Commit Number**: <!-- optional. if known - specify commit used, if built from source -->
|
||||
* **Platform**: <!-- either `uname -a` output, or if Windows, version and 32-bit or 64-bit -->
|
||||
|
||||
### Current Behavior:
|
||||
|
||||
### Expected Behavior:
|
||||
|
||||
### Suggested Fix:
|
|
@ -0,0 +1,120 @@
|
|||
*~
|
||||
# Windows
|
||||
*.user.*
|
||||
*.idea*
|
||||
*.log
|
||||
*.tlog
|
||||
*.cache
|
||||
*.obj
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.lastbuildstate
|
||||
*.unsuccessfulbuild
|
||||
*.suo
|
||||
*.res
|
||||
*.ipch
|
||||
*.manifest
|
||||
*.user
|
||||
|
||||
# Linux
|
||||
# ignore local configuration
|
||||
config.*
|
||||
config/*
|
||||
Makefile
|
||||
Makefile.in
|
||||
*.m4
|
||||
|
||||
# ignore help scripts/files
|
||||
configure
|
||||
libtool
|
||||
stamp-h1
|
||||
tesseract.pc
|
||||
config_auto.h
|
||||
/doc/html/*
|
||||
/doc/*.1
|
||||
/doc/*.5
|
||||
/doc/*.html
|
||||
/doc/*.xml
|
||||
|
||||
# generated version file
|
||||
/src/api/tess_version.h
|
||||
|
||||
# executables
|
||||
/src/api/tesseract
|
||||
/src/training/ambiguous_words
|
||||
/src/training/classifier_tester
|
||||
/src/training/cntraining
|
||||
/src/training/combine_tessdata
|
||||
/src/training/dawg2wordlist
|
||||
/src/training/merge_unicharsets
|
||||
/src/training/mftraining
|
||||
/src/training/set_unicharset_properties
|
||||
/src/training/shapeclustering
|
||||
/src/training/text2image
|
||||
/src/training/unicharset_extractor
|
||||
/src/training/wordlist2dawg
|
||||
|
||||
*.patch
|
||||
|
||||
# files generated by libtool
|
||||
/src/training/combine_lang_model
|
||||
/src/training/lstmeval
|
||||
/src/training/lstmtraining
|
||||
|
||||
# ignore compilation files
|
||||
build/*
|
||||
/bin
|
||||
*/.deps/*
|
||||
*/.libs/*
|
||||
*/*/.deps/*
|
||||
*/*/.libs/*
|
||||
*.lo
|
||||
*.la
|
||||
*.o
|
||||
*.Plo
|
||||
*.a
|
||||
*.class
|
||||
*.jar
|
||||
__pycache__
|
||||
|
||||
# tessdata
|
||||
*.traineddata
|
||||
|
||||
# OpenCL
|
||||
tesseract_opencl_profile_devices.dat
|
||||
kernel*.bin
|
||||
|
||||
# build dirs
|
||||
/build*
|
||||
/.cppan
|
||||
/cppan
|
||||
/*.dll
|
||||
/*.lib
|
||||
/*.exe
|
||||
/*.lnk
|
||||
/win*
|
||||
.vs*
|
||||
.s*
|
||||
|
||||
# files generated by "make check"
|
||||
/tests/.dirstamp
|
||||
/unittest/*.trs
|
||||
/unittest/tmp/*
|
||||
|
||||
# test programs
|
||||
/unittest/*_test
|
||||
/unittest/primesbitvector
|
||||
/unittest/primesmap
|
||||
|
||||
# generated files from unlvtests
|
||||
times.txt
|
||||
/unlvtests/results*
|
||||
|
||||
# snap packaging specific rules
|
||||
/parts/
|
||||
/stage/
|
||||
/prime/
|
||||
/snap/.snapcraft/
|
||||
|
||||
/*.snap
|
||||
/*_source.tar.bz2
|
|
@ -0,0 +1,9 @@
|
|||
[submodule "abseil"]
|
||||
path = abseil
|
||||
url = https://github.com/abseil/abseil-cpp.git
|
||||
[submodule "googletest"]
|
||||
path = googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
[submodule "test"]
|
||||
path = test
|
||||
url = https://github.com/tesseract-ocr/test
|
|
@ -0,0 +1,18 @@
|
|||
extraction:
|
||||
cpp:
|
||||
prepare:
|
||||
packages:
|
||||
- libpango1.0-dev
|
||||
configure:
|
||||
command:
|
||||
- ./autogen.sh
|
||||
- mkdir _lgtm_build_dir
|
||||
- cd _lgtm_build_dir
|
||||
- ../configure
|
||||
index:
|
||||
build_command:
|
||||
- cd _lgtm_build_dir
|
||||
- make training
|
||||
python:
|
||||
python_setup:
|
||||
version: 3
|
|
@ -0,0 +1,57 @@
|
|||
# Travis CI configuration for Tesseract
|
||||
|
||||
language: cpp
|
||||
|
||||
dist: xenial
|
||||
|
||||
env:
|
||||
- LEPT_VER=1.77.0
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
sudo: false
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
#- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- libarchive-dev
|
||||
- libpango1.0-dev
|
||||
#- g++-6
|
||||
|
||||
#matrix:
|
||||
#include:
|
||||
#- os: osx
|
||||
#install:
|
||||
#script: brew install tesseract --HEAD
|
||||
#cache:
|
||||
#directories:
|
||||
#- $HOME/Library/Caches/Homebrew
|
||||
#allow_failures:
|
||||
#- script: brew install tesseract --HEAD
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- leptonica-$LEPT_VER
|
||||
|
||||
before_install:
|
||||
- if [[ $TRAVIS_OS_NAME == linux ]]; then LINUX=true; fi
|
||||
- if [[ $TRAVIS_OS_NAME == osx ]]; then OSX=true; fi
|
||||
|
||||
install:
|
||||
#- if [[ $LINUX && "$CXX" = "g++" ]]; then export CXX="g++-6" CC="gcc-6"; fi
|
||||
- if test ! -d leptonica-$LEPT_VER/src; then curl -Ls https://github.com/DanBloomberg/leptonica/archive/$LEPT_VER.tar.gz | tar -xz; fi
|
||||
- if test ! -d leptonica-$LEPT_VER/usr; then cmake -Hleptonica-$LEPT_VER -Bleptonica-$LEPT_VER/build -DCMAKE_INSTALL_PREFIX=leptonica-$LEPT_VER/usr; fi
|
||||
- if test ! -e leptonica-$LEPT_VER/usr/lib/libleptonica.so; then make -C leptonica-$LEPT_VER/build install; fi
|
||||
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DLeptonica_DIR=leptonica-$LEPT_VER/build -DCPPAN_BUILD=OFF
|
||||
- make
|
|
@ -0,0 +1,44 @@
|
|||
Ray Smith (lead developer) <theraysmith@gmail.com>
|
||||
Ahmad Abdulkader
|
||||
Rika Antonova
|
||||
Nicholas Beato
|
||||
Jeff Breidenbach
|
||||
Samuel Charron
|
||||
Phil Cheatle
|
||||
Simon Crouch
|
||||
David Eger
|
||||
Sheelagh Huddleston
|
||||
Dan Johnson
|
||||
Rajesh Katikam
|
||||
Thomas Kielbus
|
||||
Dar-Shyang Lee
|
||||
Zongyi (Joe) Liu
|
||||
Robert Moss
|
||||
Chris Newton
|
||||
Michael Reimer
|
||||
Marius Renn
|
||||
Raquel Romano
|
||||
Christy Russon
|
||||
Shobhit Saxena
|
||||
Mark Seaman
|
||||
Faisal Shafait
|
||||
Hiroshi Takenaka
|
||||
Ranjith Unnikrishnan
|
||||
Joern Wanke
|
||||
Ping Ping Xiu
|
||||
Andrew Ziem
|
||||
Oscar Zuniga
|
||||
|
||||
Community Contributors:
|
||||
Zdenko Podobný (Maintainer)
|
||||
Jim Regan (Maintainer)
|
||||
James R Barlow
|
||||
Amit Dovev
|
||||
Martin Ettl
|
||||
Shree Devi Kumar
|
||||
Noah Metzger
|
||||
Tom Morris
|
||||
Tobias Müller
|
||||
Egor Pugin
|
||||
Sundar M. Vaidya
|
||||
Stefan Weil
|
|
@ -0,0 +1,591 @@
|
|||
#
|
||||
# tesseract
|
||||
#
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# cmake settings
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
|
||||
|
||||
# In-source builds are disabled.
|
||||
if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
message(FATAL_ERROR
|
||||
"CMake generation is not possible within the source directory!"
|
||||
"\n Remove the CMakeCache.txt file and try again from another folder, e.g.:"
|
||||
"\n "
|
||||
"\n rm CMakeCache.txt"
|
||||
"\n mkdir build"
|
||||
"\n cd build"
|
||||
"\n cmake .."
|
||||
)
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${EXECUTABLE_OUTPUT_PATH}")
|
||||
|
||||
# Use solution folders.
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMake Targets")
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# project settings
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
project(tesseract C CXX)
|
||||
|
||||
# Get version with components from VERSION file.
|
||||
file(STRINGS "VERSION" VERSION_PLAIN)
|
||||
string(REGEX REPLACE "^([^.]*)\\..*" "\\1" VERSION_MAJOR ${VERSION_PLAIN})
|
||||
string(REGEX REPLACE "^[^.]*\\.([^.]*)\\..*" "\\1" VERSION_MINOR ${VERSION_PLAIN})
|
||||
string(REGEX REPLACE "^[^.]*\\.[^.]*\\.([0-9]*).*" "\\1" VERSION_PATCH ${VERSION_PLAIN})
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
|
||||
execute_process(COMMAND git --git-dir ${CMAKE_CURRENT_SOURCE_DIR}/.git describe --abbrev=4
|
||||
OUTPUT_VARIABLE GIT_REV)
|
||||
string(REGEX REPLACE "\n$" "" PACKAGE_VERSION "${GIT_REV}")
|
||||
endif()
|
||||
if(NOT PACKAGE_VERSION)
|
||||
set(PACKAGE_VERSION ${VERSION_PLAIN})
|
||||
endif()
|
||||
|
||||
# Provide also same macro names as autoconf (see configure.ac).
|
||||
set(GENERIC_MAJOR_VERSION ${VERSION_MAJOR})
|
||||
set(GENERIC_MINOR_VERSION ${VERSION_MINOR})
|
||||
set(GENERIC_MICRO_VERSION ${VERSION_PATCH})
|
||||
|
||||
set(MINIMUM_LEPTONICA_VERSION 1.74)
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# options
|
||||
#
|
||||
###############################################################################
|
||||
message( "Configuring tesseract version ${PACKAGE_VERSION}...")
|
||||
|
||||
option(CPPAN_BUILD "Build with cppan" ON)
|
||||
option(SW_BUILD "Build with sw" OFF)
|
||||
option(OPENMP_BUILD "Build with openmp support" OFF) # see issue #1662
|
||||
option(AUTO_OPTIMIZE "Usage of cmake auto optimize macros (not suitable for portable build)" ON)
|
||||
option(GRAPHICS_DISABLED "Disable disable graphics (ScrollView)" OFF)
|
||||
option(DISABLED_LEGACY_ENGINE "Disable the legacy OCR engine" OFF)
|
||||
option(BUILD_TRAINING_TOOLS "Build training tools" ON)
|
||||
option(BUILD_TESTS "Build tests" OFF)
|
||||
|
||||
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.cppan OR SW_BUILD)
|
||||
set(CPPAN_BUILD OFF)
|
||||
endif()
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# compiler and linker
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "Setting build type to 'Release' as none was specified.")
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
|
||||
endif()
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
# Check for C++ standard to use
|
||||
get_property(known_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
|
||||
if (cxx_std_17 IN_LIST known_features)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
elseif (cxx_std_14 IN_LIST known_features)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
else() # minimum required standard
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
endif()
|
||||
|
||||
# Avoid using experimental c++1y (c++1z) standard even if the compiler announces cxx14 (cxx17)
|
||||
# in CMAKE_CXX_KNOWN_FEATURES and CMAKE_CXX_COMPILE_FEATURES
|
||||
# It is the case of clang 3.9, 4.0 (announces c++1z) and gcc 4.8 (announces c++1y)
|
||||
if ("${CMAKE_CXX17_STANDARD_COMPILE_OPTION}" STREQUAL "-std=c++1z")
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
endif()
|
||||
if ("${CMAKE_CXX14_STANDARD_COMPILE_OPTION}" STREQUAL "-std=c++1y")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
set(LIBRARY_TYPE SHARED)
|
||||
if (STATIC)
|
||||
set(LIBRARY_TYPE)
|
||||
endif()
|
||||
|
||||
# auto optimize
|
||||
if (AUTO_OPTIMIZE)
|
||||
include(OptimizeForArchitecture)
|
||||
AutodetectHostArchitecture()
|
||||
OptimizeForArchitecture()
|
||||
endif()
|
||||
# Compiler specific environments
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CLANG 1)
|
||||
endif()
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR MINGW)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -DDEBUG -pedantic -Og")
|
||||
elseif(MSVC)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
|
||||
if (NOT CLANG)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
endif()
|
||||
# Don't use /Wall because it generates too many warnings.
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /W4 /bigobj")
|
||||
endif()
|
||||
if(CLANG) # clang all platforms
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-unused-command-line-argument")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -DDEBUG -pedantic -O0")
|
||||
endif()
|
||||
|
||||
if (OPENMP_BUILD)
|
||||
find_package(OpenMP QUIET)
|
||||
if (OpenMP_FOUND)
|
||||
message(">> ${OpenMP_FOUND} ${OpenMP_VERSION}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
add_library(OpenMP::OpenMP_CXX IMPORTED INTERFACE)
|
||||
endif()
|
||||
# https://stackoverflow.com/questions/12399422/how-to-set-linker-flags-for-openmp-in-cmakes-try-compile-function
|
||||
if (NOT OpenMP_FOUND AND CLANG AND WIN32)
|
||||
# workaroung because find_package(OpenMP) does not work for clang-cl
|
||||
# https://gitlab.kitware.com/cmake/cmake/issues/19404
|
||||
check_include_file_cxx(omp.h HAVE_OMP_H_INCLUDE)
|
||||
find_library(OpenMP_LIBRARY NAMES omp libomp.lib)
|
||||
message(">> OpenMP_LIBRARY: ${OpenMP_LIBRARY}")
|
||||
if (MSVC)
|
||||
set(OpenMP_CXX_FLAGS "${OpenMP_CXX_FLAGS} /openmp")
|
||||
else()
|
||||
set(OpenMP_CXX_FLAGS "${OpenMP_CXX_FLAGS} -fopenmp")
|
||||
endif()
|
||||
set(OpenMP_FOUND 1)
|
||||
add_definitions(-D_OPENMP=201107) # 3.1 version is supported from Clang 3.8.0
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if (CYGWIN)
|
||||
add_definitions(-D__CYGWIN__)
|
||||
elseif(UNIX)
|
||||
if (NOT ANDROID)
|
||||
set(LIB_pthread pthread)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(LIB_Ws2_32 Ws2_32)
|
||||
endif()
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# packages
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
if(CPPAN_BUILD)
|
||||
if (STATIC)
|
||||
set(CPPAN_BUILD_SHARED_LIBS 0)
|
||||
else()
|
||||
set(CPPAN_BUILD_SHARED_LIBS 1)
|
||||
endif()
|
||||
add_subdirectory(.cppan)
|
||||
elseif (SW_BUILD)
|
||||
find_package(SW REQUIRED)
|
||||
if (STATIC)
|
||||
set(SW_BUILD_SHARED_LIBS 0)
|
||||
else()
|
||||
set(SW_BUILD_SHARED_LIBS 1)
|
||||
endif()
|
||||
sw_add_package(
|
||||
org.sw.demo.danbloomberg.leptonica-master
|
||||
org.sw.demo.libarchive.libarchive
|
||||
)
|
||||
if (BUILD_TRAINING_TOOLS)
|
||||
sw_add_package(
|
||||
org.sw.demo.gnome.pango.pangocairo
|
||||
org.sw.demo.unicode.icu.i18n
|
||||
)
|
||||
endif()
|
||||
sw_execute()
|
||||
else()
|
||||
find_package(PkgConfig)
|
||||
if(PKG_CONFIG_EXECUTABLE AND NOT Leptonica_DIR)
|
||||
pkg_check_modules(Leptonica REQUIRED lept>=${MINIMUM_LEPTONICA_VERSION})
|
||||
link_directories(${Leptonica_LIBRARY_DIRS})
|
||||
else()
|
||||
find_package(Leptonica ${MINIMUM_LEPTONICA_VERSION} REQUIRED CONFIG)
|
||||
endif()
|
||||
if (NOT Leptonica_FOUND)
|
||||
message(FATAL_ERROR "Cannot find required library Leptonica. Quitting!")
|
||||
endif(NOT Leptonica_FOUND)
|
||||
|
||||
find_package(LibArchive)
|
||||
if(LibArchive_FOUND)
|
||||
set(HAVE_LIBARCHIVE ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(OpenCL QUIET)
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# configure
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
foreach(flag ${Vc_ARCHITECTURE_FLAGS})
|
||||
set(Vc_CXX_FLAGS "${Vc_CXX_FLAGS} ${flag}")
|
||||
endforeach()
|
||||
|
||||
# add definition as expected in src/arch/simddetect.cpp
|
||||
set(AVX_OPT OFF)
|
||||
set(AVX2_OPT OFF)
|
||||
set(FMA_OPT OFF)
|
||||
set(SSE41_OPT OFF)
|
||||
set(MARCH_NATIVE_OPT OFF)
|
||||
foreach(flag ${_enable_vector_unit_list}) # from OptimizeForArchitecture()
|
||||
string(TOUPPER "${flag}" flag)
|
||||
string(REPLACE "\." "_" flag "${flag}")
|
||||
set(simd_flags "${simd_flags} -D${flag}")
|
||||
string(REPLACE "_" "" flag "${flag}")
|
||||
if("${flag}" MATCHES "AVX|AVX2|FMA|SSE41")
|
||||
set("${flag}_OPT" ON)
|
||||
endif()
|
||||
endforeach(flag)
|
||||
if (NOT MSVC)
|
||||
set(MARCH_NATIVE_FLAGS "${MARCH_NATIVE_FLAGS} -O3 -ffast-math")
|
||||
endif()
|
||||
CHECK_CXX_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||
if(COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||
set(MARCH_NATIVE_FLAGS "${MARCH_NATIVE_FLAGS} -march=native -mtune=native")
|
||||
set(MARCH_NATIVE_OPT ON)
|
||||
endif()
|
||||
|
||||
set(AUTOCONFIG_SRC ${CMAKE_CURRENT_BINARY_DIR}/config_auto.h.in)
|
||||
set(AUTOCONFIG ${CMAKE_CURRENT_BINARY_DIR}/config_auto.h)
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
|
||||
if(GRAPHICS_DISABLED)
|
||||
message("ScrollView debugging disabled.")
|
||||
endif()
|
||||
set (CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} "${CMAKE_PREFIX_PATH}/include" "${CMAKE_INSTALL_PREFIX}/include")
|
||||
include(Configure)
|
||||
|
||||
configure_file(${AUTOCONFIG_SRC} ${AUTOCONFIG} @ONLY)
|
||||
|
||||
set(INCLUDE_DIR "${CMAKE_INSTALL_PREFIX}/include" "${CMAKE_INSTALL_PREFIX}/include/tesseract")
|
||||
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/api/tess_version.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/api/tess_version.h @ONLY)
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/vs2010/tesseract/tesseract.rc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/vs2010/tesseract/tesseract.rc @ONLY)
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/vs2010/tesseract/libtesseract.rc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/vs2010/tesseract/libtesseract.rc @ONLY)
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/TesseractConfig-version.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/TesseractConfig-version.cmake @ONLY)
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/TesseractConfig.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/TesseractConfig.cmake @ONLY)
|
||||
|
||||
# show summary of configuration
|
||||
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
|
||||
set(COMPILER_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
elseif(${CMAKE_BUILD_TYPE} MATCHES Release)
|
||||
set(COMPILER_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
endif()
|
||||
message( STATUS )
|
||||
message( STATUS "General configuration for Tesseract ${PACKAGE_VERSION}")
|
||||
message( STATUS "--------------------------------------------------------")
|
||||
message( STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
message( STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID}")
|
||||
message( STATUS "Used standard: C++${CMAKE_CXX_STANDARD}")
|
||||
message( STATUS "CXX compiler options: ${COMPILER_FLAGS}")
|
||||
message( STATUS "Linker options: ${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UP}}")
|
||||
message( STATUS "Install directory: ${CMAKE_INSTALL_PREFIX}")
|
||||
message( STATUS "Architecture flags: ${Vc_ARCHITECTURE_FLAGS}")
|
||||
message( STATUS "Vector unit list: ${_enable_vector_unit_list}")
|
||||
message( STATUS "AVX_OPT: ${AVX_OPT}")
|
||||
message( STATUS "AVX2_OPT: ${AVX2_OPT}")
|
||||
message( STATUS "FMA_OPT: ${FMA_OPT}")
|
||||
message( STATUS "SSE41_OPT: ${SSE41_OPT}")
|
||||
message( STATUS "MARCH_NATIVE_OPT: ${MARCH_NATIVE_OPT}")
|
||||
message( STATUS "simd_flags: ${simd_flags}")
|
||||
message( STATUS "--------------------------------------------------------")
|
||||
message( STATUS "Build with cppan [CPPAN_BUILD]: ${CPPAN_BUILD}")
|
||||
if (CPPAN_BUILD)
|
||||
message( STATUS "##################################################################################")
|
||||
message( "!! CPPAN is depreciated! Please consider switching to SW Build.\n"
|
||||
" More details: https://github.com/tesseract-ocr/tesseract/wiki/Compiling#windows")
|
||||
message( STATUS "##################################################################################")
|
||||
endif()
|
||||
message( STATUS "Build with sw [SW_BUILD]: ${SW_BUILD}")
|
||||
message( STATUS "Build with openmp support [OPENMP_BUILD]: ${OPENMP_BUILD}")
|
||||
message( STATUS "Disable disable graphics (ScrollView) [GRAPHICS_DISABLED]: ${GRAPHICS_DISABLED}")
|
||||
message( STATUS "Disable the legacy OCR engine [DISABLED_LEGACY_ENGINE]: ${DISABLED_LEGACY_ENGINE}")
|
||||
message( STATUS "Build training tools [BUILD_TRAINING_TOOLS]: ${BUILD_TRAINING_TOOLS}")
|
||||
message( STATUS "Build tests [BUILD_TESTS]: ${BUILD_TESTS}")
|
||||
message( STATUS "--------------------------------------------------------")
|
||||
message( STATUS )
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# build
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
include(BuildFunctions)
|
||||
include(SourceGroups)
|
||||
|
||||
add_definitions(-D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS=1)
|
||||
|
||||
include_directories(${Leptonica_INCLUDE_DIRS})
|
||||
include_directories(${LibArchive_INCLUDE_DIRS})
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
include_directories(src/api)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/api)
|
||||
include_directories(src/arch)
|
||||
include_directories(src/ccmain)
|
||||
include_directories(src/ccstruct)
|
||||
include_directories(src/ccutil)
|
||||
include_directories(src/classify)
|
||||
include_directories(src/cutil)
|
||||
include_directories(src/dict)
|
||||
include_directories(src/lstm)
|
||||
include_directories(src/opencl)
|
||||
include_directories(src/textord)
|
||||
include_directories(src/viewer)
|
||||
include_directories(src/wordrec)
|
||||
include_directories(src/training)
|
||||
if(ANDROID_TOOLCHAIN)
|
||||
include_directories(${ANDROID_TOOLCHAIN}/sysroot/usr/include)
|
||||
add_compile_definitions(__ANDROID_API_FUTURE__)
|
||||
endif()
|
||||
|
||||
########################################
|
||||
# LIBRARY tesseract
|
||||
########################################
|
||||
|
||||
file(GLOB tesseract_src
|
||||
src/ccmain/*.cpp
|
||||
src/ccstruct/*.cpp
|
||||
src/ccutil/*.cpp
|
||||
src/classify/*.cpp
|
||||
src/cutil/*.cpp
|
||||
src/dict/*.cpp
|
||||
src/lstm/*.cpp
|
||||
src/opencl/*.cpp
|
||||
src/textord/*.cpp
|
||||
src/viewer/*.cpp
|
||||
src/wordrec/*.cpp
|
||||
)
|
||||
|
||||
list(APPEND arch_files
|
||||
src/arch/dotproduct.cpp
|
||||
src/arch/simddetect.cpp
|
||||
src/arch/intsimdmatrix.cpp
|
||||
)
|
||||
set_source_files_properties(${arch_files} PROPERTIES COMPILE_FLAGS "${simd_flags}")
|
||||
set_source_files_properties(src/arch/dotproduct.cpp PROPERTIES COMPILE_FLAGS "${MARCH_NATIVE_FLAGS} ${Vc_CXX_FLAGS}")
|
||||
if(AVX_OPT)
|
||||
list(APPEND arch_files_opt src/arch/dotproductavx.cpp)
|
||||
set_source_files_properties(src/arch/dotproductavx.cpp PROPERTIES COMPILE_FLAGS "-DAVX")
|
||||
endif(AVX_OPT)
|
||||
if(AVX2_OPT)
|
||||
list(APPEND arch_files_opt src/arch/intsimdmatrixavx2.cpp)
|
||||
set_source_files_properties(src/arch/intsimdmatrixavx2.cpp PROPERTIES COMPILE_FLAGS "-DAVX2")
|
||||
endif(AVX2_OPT)
|
||||
if(FMA_OPT)
|
||||
list(APPEND arch_files_opt src/arch/dotproductfma.cpp)
|
||||
set_source_files_properties(src/arch/dotproductfma.cpp PROPERTIES COMPILE_FLAGS "-mfma")
|
||||
endif(FMA_OPT)
|
||||
if(SSE41_OPT)
|
||||
list(APPEND arch_files_opt src/arch/dotproductsse.cpp src/arch/intsimdmatrixsse.cpp)
|
||||
set_source_files_properties(src/arch/dotproductsse.cpp src/arch/intsimdmatrixsse.cpp PROPERTIES COMPILE_FLAGS "-DSSE4_1 -msse4.1")
|
||||
endif(SSE41_OPT)
|
||||
set_source_files_properties(${arch_files_opt} PROPERTIES COMPILE_FLAGS "${Vc_CXX_FLAGS}")
|
||||
|
||||
file(GLOB tesseract_hdr
|
||||
src/api/*.h
|
||||
src/arch/*.h
|
||||
src/ccmain/*.h
|
||||
src/ccstruct/*.h
|
||||
src/ccutil/*.h
|
||||
src/classify/*.h
|
||||
src/cutil/*.h
|
||||
src/dict/*.h
|
||||
src/lstm/*.h
|
||||
src/opencl/*.h
|
||||
src/textord/*.h
|
||||
src/viewer/*.h
|
||||
src/wordrec/*.h
|
||||
)
|
||||
|
||||
set(tesseract_src ${tesseract_src}
|
||||
src/api/baseapi.cpp
|
||||
src/api/capi.cpp
|
||||
src/api/renderer.cpp
|
||||
src/api/altorenderer.cpp
|
||||
src/api/hocrrenderer.cpp
|
||||
src/api/lstmboxrenderer.cpp
|
||||
src/api/pdfrenderer.cpp
|
||||
src/api/wordstrboxrenderer.cpp
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
if (MSVC)
|
||||
include_directories(src/vs2010/tesseract)
|
||||
set(tesseract_hdr
|
||||
${tesseract_hdr}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/vs2010/tesseract/resource.h)
|
||||
set(tesseract_rsc ${CMAKE_CURRENT_BINARY_DIR}/vs2010/tesseract/libtesseract.rc)
|
||||
endif() # MSVC
|
||||
endif()
|
||||
|
||||
add_library (libtesseract ${LIBRARY_TYPE} ${tesseract_src} ${arch_files}
|
||||
${arch_files_opt} ${tesseract_hdr} ${tesseract_rsc}
|
||||
)
|
||||
if (NOT STATIC)
|
||||
target_compile_definitions (libtesseract
|
||||
PRIVATE -DTESS_EXPORTS
|
||||
INTERFACE -DTESS_IMPORTS
|
||||
)
|
||||
set_target_properties (libtesseract PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS True)
|
||||
endif()
|
||||
target_link_libraries (libtesseract PRIVATE ${LIB_Ws2_32} ${LIB_pthread})
|
||||
set_target_properties (libtesseract PROPERTIES VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
|
||||
set_target_properties (libtesseract PROPERTIES SOVERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
|
||||
if (WIN32)
|
||||
set_target_properties (libtesseract PROPERTIES OUTPUT_NAME tesseract${VERSION_MAJOR}${VERSION_MINOR})
|
||||
set_target_properties (libtesseract PROPERTIES DEBUG_OUTPUT_NAME tesseract${VERSION_MAJOR}${VERSION_MINOR}d)
|
||||
else()
|
||||
set_target_properties (libtesseract PROPERTIES OUTPUT_NAME tesseract)
|
||||
endif()
|
||||
|
||||
if (CPPAN_BUILD)
|
||||
target_link_libraries (libtesseract PUBLIC
|
||||
pvt.cppan.demo.danbloomberg.leptonica
|
||||
pvt.cppan.demo.libarchive.libarchive
|
||||
)
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/TesseractTargets.cmake "include(${CMAKE_CURRENT_BINARY_DIR}/cppan.cmake)\n")
|
||||
export(TARGETS libtesseract APPEND FILE ${CMAKE_CURRENT_BINARY_DIR}/TesseractTargets.cmake)
|
||||
elseif (SW_BUILD)
|
||||
target_link_libraries (libtesseract PUBLIC
|
||||
org.sw.demo.danbloomberg.leptonica-master
|
||||
org.sw.demo.libarchive.libarchive
|
||||
)
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/TesseractTargets.cmake "include(${CMAKE_CURRENT_BINARY_DIR}/cppan.cmake)\n")
|
||||
export(TARGETS libtesseract APPEND FILE ${CMAKE_CURRENT_BINARY_DIR}/TesseractTargets.cmake)
|
||||
else()
|
||||
target_link_libraries (libtesseract PUBLIC
|
||||
${Leptonica_LIBRARIES}
|
||||
${LibArchive_LIBRARIES}
|
||||
)
|
||||
export(TARGETS libtesseract FILE ${CMAKE_CURRENT_BINARY_DIR}/TesseractTargets.cmake)
|
||||
endif()
|
||||
|
||||
if (WIN32 AND CLANG AND OPENMP_BUILD)
|
||||
# Workaround for "libomp.lib is not automatically added on Windows"
|
||||
# see: http://lists.llvm.org/pipermail/openmp-dev/2015-August/000857.html
|
||||
target_link_libraries (libtesseract PRIVATE ${OpenMP_LIBRARY})
|
||||
endif()
|
||||
|
||||
########################################
|
||||
# EXECUTABLE tesseractmain
|
||||
########################################
|
||||
|
||||
set(tesseractmain_src src/api/tesseractmain.cpp)
|
||||
if (MSVC)
|
||||
set(tesseractmain_rsc ${CMAKE_CURRENT_BINARY_DIR}/vs2010/tesseract/tesseract.rc)
|
||||
endif()
|
||||
|
||||
add_executable (tesseract ${tesseractmain_src} ${tesseractmain_rsc})
|
||||
target_link_libraries (tesseract libtesseract)
|
||||
if (HAVE_TIFFIO_H)
|
||||
target_link_libraries(tesseract tiff)
|
||||
endif()
|
||||
|
||||
if (OPENMP_BUILD AND UNIX)
|
||||
target_link_libraries (tesseract pthread)
|
||||
endif()
|
||||
|
||||
########################################
|
||||
|
||||
if (BUILD_TESTS AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/googletest/CMakeLists.txt)
|
||||
add_subdirectory(googletest)
|
||||
endif()
|
||||
|
||||
if (BUILD_TRAINING_TOOLS)
|
||||
add_subdirectory(src/training)
|
||||
endif()
|
||||
|
||||
get_target_property(tesseract_NAME libtesseract NAME)
|
||||
get_target_property(tesseract_VERSION libtesseract VERSION)
|
||||
get_target_property(tesseract_OUTPUT_NAME libtesseract OUTPUT_NAME)
|
||||
configure_file(tesseract.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/tesseract.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/tesseract.pc DESTINATION lib/pkgconfig)
|
||||
install(TARGETS tesseract RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
|
||||
install(TARGETS libtesseract EXPORT TesseractTargets RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
|
||||
install(EXPORT TesseractTargets DESTINATION cmake)
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/TesseractConfig.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/TesseractConfig-version.cmake
|
||||
DESTINATION cmake)
|
||||
|
||||
install(FILES
|
||||
# from api/makefile.am
|
||||
src/api/apitypes.h
|
||||
src/api/baseapi.h
|
||||
src/api/capi.h
|
||||
src/api/renderer.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/api/tess_version.h
|
||||
|
||||
#from ccmain/makefile.am
|
||||
src/ccmain/thresholder.h
|
||||
src/ccmain/ltrresultiterator.h
|
||||
src/ccmain/pageiterator.h
|
||||
src/ccmain/resultiterator.h
|
||||
src/ccmain/osdetect.h
|
||||
|
||||
#from ccstruct/makefile.am
|
||||
src/ccstruct/publictypes.h
|
||||
|
||||
#from ccutil/makefile.am
|
||||
src/ccutil/genericvector.h
|
||||
src/ccutil/helpers.h
|
||||
src/ccutil/ocrclass.h
|
||||
src/ccutil/platform.h
|
||||
src/ccutil/serialis.h
|
||||
src/ccutil/strngs.h
|
||||
src/ccutil/tesscallback.h
|
||||
src/ccutil/unichar.h
|
||||
#${CMAKE_CURRENT_BINARY_DIR}/src/endianness.h
|
||||
DESTINATION include/tesseract)
|
||||
|
||||
########################################
|
||||
# uninstall target
|
||||
########################################
|
||||
if(NOT TARGET uninstall)
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
endif()
|
||||
|
||||
###############################################################################
|
|
@ -0,0 +1,80 @@
|
|||
# Contributing
|
||||
|
||||
**Please follow these rules and advice**.
|
||||
|
||||
## Creating an Issue or Using the Forum
|
||||
|
||||
If you think you found a bug in Tesseract, please create an issue.
|
||||
|
||||
Use the [users mailing-list](https://groups.google.com/d/forum/tesseract-ocr) instead of creating an Issue if ...
|
||||
* You have problems using Tesseract and need some help.
|
||||
* You have problems installing the software.
|
||||
* You are not satisfied with the accuracy of the OCR, and want to ask how you can improve it. Note: You should first read the [ImproveQuality](https://github.com/tesseract-ocr/tesseract/wiki/ImproveQuality) wiki page.
|
||||
* You are trying to train Tesseract and you have a problem and/or want to ask a question about the training process. Note: You should first read the **official** guides [[1]](https://github.com/tesseract-ocr/tesseract/wiki) or [[2]](https://github.com/tesseract-ocr/tesseract/wiki/TrainingTesseract) found in the project wiki.
|
||||
* You have a general question.
|
||||
|
||||
An issue should only be reported if the platform you are using is one of these:
|
||||
* Linux (but not a version that is more than 4 years old)
|
||||
* Windows (Windows 7 or newer version)
|
||||
* macOS (last 3 releases)
|
||||
|
||||
For older versions or other operating systems, use the Tesseract forum.
|
||||
|
||||
When creating an issue, please report your operating system, including its specific version: "Ubuntu 16.04", "Windows 10", "Mac OS X 10.11" etc.
|
||||
|
||||
Search through open and closed issues to see if similar issue has been reported already (and sometimes also has been solved).
|
||||
|
||||
Similarly, before you post your question in the forum, search through past threads to see if similar question has been asked already.
|
||||
|
||||
Read the [wiki](https://github.com/tesseract-ocr/tesseract/wiki) before you report your issue or ask a question in the forum.
|
||||
|
||||
Only report an issue in the latest official release. Optionally, try to check if the issue is not already solved in the latest snapshot in the git repository.
|
||||
|
||||
Make sure you are able to replicate the problem with Tesseract command line program. For external programs that use Tesseract (including wrappers and your own program, if you are developer), report the issue to the developers of that software if it's possible. You can also try to find help in the Tesseract forum.
|
||||
|
||||
Each version of Tesseract has its own language data you need to obtain. You **must** obtain and install trained data for English (eng) and osd. Verify that Tesseract knows about these two files (and other trained data you installed) with this command:
|
||||
`tesseract --list-langs`.
|
||||
|
||||
Post example files to demonstrate the problem.
|
||||
BUT don't post files with private info (about yourself or others).
|
||||
|
||||
When attaching a file to the issue report / forum ...
|
||||
* Do not post a file larger than 20 MB.
|
||||
* GitHub supports only few file name extensions like `.png` or `.txt`. If GitHub rejects your files, you can compress them using a program that can produce a zip archive and then load this zip file to GitHub.
|
||||
|
||||
Do not attach programs or libraries to your issues/posts.
|
||||
|
||||
For large files or for programs, add a link to a location where they can be downloaded (your site, Git repo, Google Drive, Dropbox etc.)
|
||||
|
||||
Attaching a multi-page TIFF image is useful only if you have problem with multi-page functionality, otherwise attach only one or a few single page images.
|
||||
|
||||
Copy the error message from the console instead of sending a screenshot of it.
|
||||
|
||||
Use the toolbar above the comment edit area to format your comment.
|
||||
|
||||
Add three backticks before and after a code sample or output of a command to format it (The `Insert code` button can help you doing it).
|
||||
|
||||
If your comment includes a code sample or output of a command that exceeds ~25 lines, post it as attached text file (`filename.txt`).
|
||||
|
||||
Use `Preview` before you send your issue. Read it again before sending.
|
||||
|
||||
Note that most of the people that respond to issues and answer questions are either other 'regular' users or **volunteers** developers. Please be nice to them :-)
|
||||
|
||||
The [tesseract developers](http://groups.google.com/group/tesseract-dev/) forum should be used to discuss Tesseract development: bug fixes, enhancements, add-ons for Tesseract.
|
||||
|
||||
Sometimes you will not get a respond to your issue or question. We apologize in advance! Please don't take it personally. There can be many reasons for this, including: time limits, no one knows the answer (at least not the ones that are available at that time) or just that
|
||||
your question has been asked (and has been answered) many times before...
|
||||
|
||||
## For Developers: Creating a Pull Request
|
||||
|
||||
You should always make sure your changes build and run successfully.
|
||||
|
||||
For that, your clone needs to have all submodules (`abseil`, `googletest`, `test`) included. To do so, either specify `--recurse-submodules` during the initial clone, or run `git submodule update --init --recursive NAME` for each `NAME` later. If `configure` already created those directories (blocking the clone), remove them first (or `make distclean`), then clone and reconfigure.
|
||||
|
||||
Have a look at [the README](./README.md) and [testing README](./test/testing/README.md) and the [wiki page](https://github.com/tesseract-ocr/tesseract/wiki/Compiling-%E2%80%93-GitInstallation#unit-test-builds) on installation.
|
||||
|
||||
In short, after running `configure` from the build directory of your choice, to build the library and CLI, run `make`. To test it, run `make check`. To build the training tools, run `make training`.
|
||||
|
||||
As soon as your changes are building and tests are succeeding, you can publish them. If you have not already, please [fork](https://guides.github.com/activities/forking/) tesseract (somewhere) on GitHub, and push your changes to that fork (in a new branch). Then [submit as PR](https://help.github.com/en/articles/creating-a-pull-request-from-a-fork).
|
||||
|
||||
Please also keep track of reports from CI (automated build status) and Coverity/LGTM (quality scan). When the indicators show deterioration after your changes, further action may be required to improve them.
|
|
@ -0,0 +1,303 @@
|
|||
2021-11-15 - V4.1.3
|
||||
* Fix build regression for autoconf build
|
||||
|
||||
2021-11-14 - V4.1.2
|
||||
* Add RowAttributes getter to PageIterator
|
||||
* Allow line images with larger width for training
|
||||
* Fix memory leaks
|
||||
* Improve build process
|
||||
* Don't output empty ALTO sourceImageInformation (issue #2700)
|
||||
* Extend URI support for Tesseract with libcurl
|
||||
* Abort LSTM training with integer model (fixes issue #1573)
|
||||
* Update documentation
|
||||
* Make automake builds less noisy by default
|
||||
* Don't use -march=native in automake builds
|
||||
|
||||
2019-12-26 - V4.1.1
|
||||
* Implemented sw build (cppan is depreciated)
|
||||
* Improved cmake build
|
||||
* Code cleanup and optimization
|
||||
* A lot of bug fixes...
|
||||
|
||||
2019-07-07 - V4.1.0
|
||||
* Added new renders Alto, LSTMBox, WordStrBox.
|
||||
* Added character boxes in hOCR output.
|
||||
* Added python training scripts (experimental) as alternative shell scripts.
|
||||
* Better support AVX / AVX2 / SSE.
|
||||
* Disable OpenMP support by default (see e.g. #1171, #1081).
|
||||
* Fix for bounding box problem.
|
||||
* Implemented support for whitelist/blacklist in LSTM engine.
|
||||
* Improved cmake configuration.
|
||||
* Code modernization and improvements.
|
||||
* A lot of bug fixes...
|
||||
|
||||
2018-10-29 - V4.0.0
|
||||
* Added new neural network system based on LSTMs, with major accuracy gains.
|
||||
* Improvements to PDF rendering.
|
||||
* Fixes to trainingdata rendering.
|
||||
* Added LSTM models+lang models to 101 languages. (tessdata repository)
|
||||
* Improved multi-page TIFF handling.
|
||||
* Fixed damage to binary images when processing PDFs.
|
||||
* Fixes to training process to allow incremental training from a recognition model.
|
||||
* Made LSTM the default engine, pushed cube out.
|
||||
* Deleted cube code.
|
||||
* Changed OEModes --oem 0 for legacy tesseract engine, --oem 1 for LSTM, --oem 2 for both, --oem 3 for default.
|
||||
* Avoid use of Leptonica debug parameters or functions.
|
||||
* Fixed multi-language mode.
|
||||
* Removed support for VS2010.
|
||||
* Added Support for VS2015 and VS2017 with CPPAN.
|
||||
* Implemented invisible text only for PDF.
|
||||
* Added AVX / SSE support for Windows.
|
||||
* Enabled OpenMP support.
|
||||
* Parameter unlv_tilde_crunching change to false.
|
||||
* Miscellaneous Fixes.
|
||||
* Detailed Changelog can be found at https://github.com/tesseract-ocr/tesseract/wiki/4.0x-Changelog and https://github.com/tesseract-ocr/tesseract/wiki/ReleaseNotes#tesseract-release-notes-oct-29-2018---v400
|
||||
|
||||
2017-02-16 - V3.05.00
|
||||
* Made some fine tuning to the hOCR output.
|
||||
* Added TSV as another optional output format.
|
||||
* Fixed ABI break introduced in 3.04.00 with the AnalyseLayout() method.
|
||||
* text2image tool - Enable all OpenType ligatures available in a font. This feature requires Pango 1.38 or newer.
|
||||
* Training tools - Replaced asserts with tprintf() and exit(1).
|
||||
* Fixed Cygwin compatibility.
|
||||
* Improved multipage tiff processing.
|
||||
* Improved the embedded pdf font (pdf.ttf).
|
||||
* Enable selection of OCR engine mode from command line.
|
||||
* Changed tesseract command line parameter '-psm' to '--psm'.
|
||||
* Write output of tesseract --help, --version and --list-langs to stdout instead of stderr.
|
||||
* Added new C API for orientation and script detection, removed the old one.
|
||||
* Increased minimum autoconf version to 2.59.
|
||||
* Removed dead code.
|
||||
* Require Leptonica 1.74 or higher.
|
||||
* Fixed many compiler warning.
|
||||
* Fixed memory and resource leaks.
|
||||
* Fixed some issues with the 'Cube' OCR engine.
|
||||
* Fixed some openCL issues.
|
||||
* Added option to build Tesseract with CMake build system.
|
||||
* Implemented CPPAN support for easy Windows building.
|
||||
|
||||
2016-02-17 - V3.04.01
|
||||
* Added OSD renderer for psm 0. Works for single page and multi-page images.
|
||||
* Improve tesstrain.sh script.
|
||||
* Simplify build and run of ScrollView.
|
||||
* Improved PDF output for OS X Preview utility.
|
||||
* INCOMPATIBLE fix to hOCR line height information - commit 134ebc3.
|
||||
* Added option to build Tesseract without Cube OCR engine (-DNO_CUBE_BUILD).
|
||||
* Enable OpenMP support.
|
||||
* Many bug fixes.
|
||||
|
||||
2015-07-11 - V3.04.00
|
||||
* Tesseract development is now done with Git and hosted at github.com (Previously we used Subversion as a VCS and code.google.com for hosting).
|
||||
* Tesseract now requires leptonica 1.71 or a higher version.
|
||||
* Removed official support for VS 2008.
|
||||
* Added support for 39 additional scripts/languages, including: amh, asm, aze_cyrl, bod, bos, ceb, cym, dzo, fas, gle, guj, hat, iku, jav, kat, kat_old, kaz, khm, kir, kur, lao, lat, mar, mya, nep, ori, pan, pus, san, sin, srp_latn, syr, tgk, tir, uig, urd, uzb, uzb_cyrl, yid
|
||||
* Major updates to training system as a result of extensive testing on 100 languages.
|
||||
* New training data for over 100 languages
|
||||
* Improved performance with PIC compilation option.
|
||||
* Significant change to invisible font system in pdf output to improve correctness and compatibility with external programs, particularly ghostscript.
|
||||
* Improved font identification.
|
||||
* Major change to improve layout analysis for heavily diacritic languages: Thai, Vietnamese, Kannada, Telugu etc.
|
||||
* Fixed problems with shifted baselines so recognition can recover from layout analysis errors.
|
||||
* Major refactor to improve speed on difficult images, especially when running a heap checker.
|
||||
* Moved params from global in page layout to tesseractclass.
|
||||
* Improved single column layout analysis.
|
||||
* Allow ocr output to multiple formats using tesseract command line executable.
|
||||
* Fixed issues with mixed eng+ara scripts.
|
||||
* Improved script consistency in numbers.
|
||||
* Major refactor of control.cpp to enable line recognition.
|
||||
* Added tesstrain.sh - a master training script.
|
||||
* Added ability to text2image training tool to just list available fonts.
|
||||
* Added ability to text2image to underline words.
|
||||
* Improved efficiency of image processing for PDF output.
|
||||
* Added parameter description for each parameter listed with 'print-parameters' command line option.
|
||||
* Added font info to hOCR output.
|
||||
* Enabled streaming input and output of multi-page documents.
|
||||
* Many bug fixes.
|
||||
|
||||
2014-02-04 - V3.03(rc1)
|
||||
* Added new training tool text2image to generate box/tif file pairs from
|
||||
text and truetype fonts.
|
||||
* Added support for PDF output with searchable text.
|
||||
* Removed entire IMAGE class and all code in image directory.
|
||||
* Tesseract executable: support for output to stdout; limited support for one
|
||||
page images from stdin (especially on Windows)
|
||||
* Added Renderer to API to allow document-level processing and output
|
||||
of document formats, like hOCR, PDF.
|
||||
* Major refactor of word-level recognition, beam search, eliminating dead code.
|
||||
* Refactored classifier to make it easier to add new ones.
|
||||
* Generalized feature extractor to allow feature extraction from greyscale.
|
||||
* Improved sub/superscript treatment.
|
||||
* Improved baseline fit.
|
||||
* Added set_unicharset_properties to training tools.
|
||||
* Many bug fixes.
|
||||
* More training source data included.
|
||||
|
||||
2012-02-01 - V3.02
|
||||
* Moved ResultIterator/PageIterator to ccmain.
|
||||
* Added Right-to-left/Bidi capability in the output iterators for Hebrew/Arabic.
|
||||
* Added paragraph detection in layout analysis/post OCR.
|
||||
* Fixed inconsistent xheight during training and over-chopping.
|
||||
* Added simultaneous multi-language capability.
|
||||
* Refactored top-level word recognition module.
|
||||
* Added experimental equation detector.
|
||||
* Improved handling of resolution from input images.
|
||||
* Blamer module added for error analysis.
|
||||
* Cleaned up externally used namespace by removing includes from baseapi.h.
|
||||
* Removed dead memory mangagement code.
|
||||
* Tidied up constraints on control parameters.
|
||||
* Added support for ShapeTable in classifier and training.
|
||||
* Refactored class pruner.
|
||||
* Fixed training leaks and randomness.
|
||||
* Major improvements to layout analysis for better image detection, diacritic detection, better textline finding, better tabstop finding.
|
||||
* Improved line detection and removal.
|
||||
* Added fixed pitch chopper for CJK.
|
||||
* Added UNICHARSET to WERD_CHOICE to make mult-language handling easier.
|
||||
* Fixed problems with internally scaled images.
|
||||
* Added page and bbox to string in tr files to identify source of training data better.
|
||||
* Fixes to Hindi Shiroreka splitter.
|
||||
* Added word bigram correction.
|
||||
* Reduced stack memory consumption and eliminated some ugly typedefs.
|
||||
* Added new uniform classifier API.
|
||||
* Added new training error counter.
|
||||
* Fixed endian bug in dawg reader.
|
||||
* Many other fixes, including the way in which the chopper finds chops and messes with the outline while it does so.
|
||||
|
||||
2010-11-29 - V3.01
|
||||
* Removed old/dead serialise/deserialze methods on *LISTIZED classes.
|
||||
* Total rewrite of DENORM to better encapsulate operation and make
|
||||
for potential to extract features from images.
|
||||
* Thread-safety! Moved all critical global and static variables to members of the appropriate class. Tesseract is now thread-safe (multiple instances can be used in parallel in multiple threads.) with the minor exception that some control parameters are still global and affect all threads.
|
||||
* Added Cube, a new recognizer for Arabic. Cube can also be used in combination with normal Tesseract for other languages with an improvement in accuracy at the cost of (much) lower speed. *There is no training module for Cube yet.*
|
||||
* `OcrEngineMode` in `Init` replaces `AccuracyVSpeed` to control cube.
|
||||
* Greatly improved segmentation search with consequent accuracy and speed improvements, especially for Chinese.
|
||||
* Added `PageIterator` and `ResultIterator` as cleaner ways to get the full results out of Tesseract, that are not currently provided by any of the `TessBaseAPI::Get*` methods. All other methods, such as the `ETEXT_STRUCT` in particular are deprecated and will be deleted in the future.
|
||||
* ApplyBoxes totally rewritten to make training easier. It can now cope with touching/overlapping training characters, and a new boxfile format allows word boxes instead of character boxes, BUT to use that you have to have already bootstrapped the language with character boxes. "Cyclic dependency" on traineddata.
|
||||
* Auto orientation and script detection added to page layout analysis.
|
||||
* Deleted *lots* of dead code.
|
||||
* Fixxht module replaced with scalable data-driven module.
|
||||
* Output font characteristics accuracy improved.
|
||||
* Removed the double conversion at each classification.
|
||||
* Upgraded oldest structs to be classes and deprecated PBLOB.
|
||||
* Removed non-deterministic baseline fit.
|
||||
* Added fixed length dawgs for Chinese.
|
||||
* Handling of vertical text improved.
|
||||
* Handling of leader dots improved.
|
||||
* Table detection greatly improved.
|
||||
* Fixed a couple of memory leaks.
|
||||
* Fixed font labels on output text. (Not perfect, but a lot better than before.)
|
||||
* Cleanup and more bug fixes
|
||||
* Special treatments for Hindi.
|
||||
* Support for build in VS2010 with Microsoft Windows SDK for Windows 7 (thanks to Michael Lutz)
|
||||
|
||||
2010-09-21 - V3.00
|
||||
* Preparations for thread safety:
|
||||
* Changed TessBaseAPI methods to be non-static
|
||||
* Created a class hierarchy for the directories to hold instance data,
|
||||
and began moving code into the classes.
|
||||
* Moved thresholding code to a separate class.
|
||||
* Added major new page layout analysis module.
|
||||
* Added HOCR output (issues 221, 263: thanks to amkryukov).
|
||||
* Added Leptonica as main image I/O and handling. Currently optional,
|
||||
but in future releases linking with Leptonica will be mandatory.
|
||||
* Ambiguity table rewritten to allow definite replacements in place
|
||||
of fix_quotes.
|
||||
* Added TessdataManager to combine data files into a single file.
|
||||
* Some dead code deleted.
|
||||
* VC++6 no longer supported. It can't cope with the use of templates.
|
||||
* Many more languages added.
|
||||
* Doxygenation of most of the function header comments.
|
||||
* Added man pages.
|
||||
* Added bash completion script (issue 247: thanks to neskiem)
|
||||
* Fix integer overview in thresholding (issue 366: thanks to Cyanide.Drake)
|
||||
* Add Danish Fraktur support (issues 300, 360: thanks to
|
||||
dsl602230@vip.cybercity.dk)
|
||||
* Fix file pointer leak (issue 359, thanks to yukihiro.nakadaira)
|
||||
* Fix an error using user-words (Issue 345: thanks to max.markin)
|
||||
* Fix a memory leak in tablefind.cpp (Issue 342, thanks to zdravco)
|
||||
* Fix a segfault due to double fclose (Issue 320, thanks to souther)
|
||||
* Fix an automake error (Issue 318, thanks to ichanjz)
|
||||
* Fix a Win32 crash on fileFormatIsTiff() (Issues 304, 316, 317, 330, 347,
|
||||
349, 352: thanks to nguyenq87, max.markin, zdenop)
|
||||
* Fixed a number of errors in newer (stricter) versions of VC++ (Issues
|
||||
301, among others)
|
||||
|
||||
2009-06-30 - V2.04
|
||||
* Integrated bug fixes and patches and misc changes for portability.
|
||||
* Integrated a patch to remove some of the "access" macros.
|
||||
* Removed dependence on lua from the viewer, speeding it up
|
||||
dramatically.
|
||||
* Fixed the viewer so it compiles and runs properly!
|
||||
* Specifically fixing issues: 1, 63, 67, 71, 76, 81, 82, 106, 111,
|
||||
112, 128, 129, 130, 133, 135, 142, 143, 145, 147, 153, 154, 160,
|
||||
165, 170, 175, 177, 187, 192, 195, 199, 201, 205, 209, 108, 169
|
||||
|
||||
2008-04-22 - V2.03
|
||||
* Fixed crash introduced in 2.02.
|
||||
* Fixed lack of tessembedded.cpp in distribution.
|
||||
* Added test for leptonica header files and conditional test for lib.
|
||||
|
||||
2008-04-21 - V2.02 (again)
|
||||
* Fixed namespace collisions with jpeg library (INT32).
|
||||
* Portability fixes for Windows for new code.
|
||||
* Updates to autoconf system for new code.
|
||||
|
||||
2008-01-23 - V2.02
|
||||
* Improvements to clustering, training and classifier.
|
||||
* Major internationalization improvements for large-character-set
|
||||
* languages, eg Kannada.
|
||||
* Removed some compiler warnings.
|
||||
* Added multipage tiff support for training and running.
|
||||
* Updated graphics output to talk to new java-based viewer.
|
||||
* Added ability to save n-best lists.
|
||||
* Added leptonica support for more file types.
|
||||
* Improved Init/End to make them safe.
|
||||
* Reduced memory use of dictionaries.
|
||||
* Added some new APIs to TessBaseAPI.
|
||||
|
||||
2007-08-27 - V2.01
|
||||
* Fixed UTF8 input problems with box file reader.
|
||||
* Fixed various infinite loops and crashes in dawg code.
|
||||
* Removed include of config_auto.h from host.h.
|
||||
* Added automatic wctype encoding to unicharset_extractor.
|
||||
* Fixed dawg table too full error.
|
||||
* Removed svn files from tarball.
|
||||
* Added new functions to tessdll.
|
||||
* Increased maximum utf8 string in a classification result to 8.
|
||||
|
||||
2007-07-02 - V2.00
|
||||
* Converted internal character handling to UTF8.
|
||||
* Trained with 6 languages.
|
||||
* Added unicharset_extractor, wordlist2dawg.
|
||||
* Added boxfile creation mode.
|
||||
* Added UNLV regression test capability.
|
||||
* Fixed problems with copyright and registered symbols.
|
||||
* Fixed extern "C" declarations problem.
|
||||
|
||||
2007-05-15 - V1.04
|
||||
* Added dll exports for Windows.
|
||||
* Fixed name collisions with stl etc.
|
||||
* Made some preliminary changes ready for unicodeization.
|
||||
* Several bug fixes discovered during unicodeization.
|
||||
|
||||
2007-02-02 - 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.
|
||||
|
||||
2006-10-04 - V1.02
|
||||
* Removed dependency on Aspirin.
|
||||
* Fixed a few missing Apache license headers.
|
||||
* Removed $log.
|
||||
|
||||
2006-09-07 - 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.
|
||||
|
||||
2006-06-16 - V1.0 of open source Tesseract checked-in.
|
|
@ -0,0 +1,17 @@
|
|||
# Dockerfile for local Travis build test
|
||||
|
||||
FROM ubuntu
|
||||
LABEL maintainer="Ian Blenke <ian@blenke.com>"
|
||||
|
||||
RUN apt-get update
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y cmake curl git ruby bundler wget unzip \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN gem install bundler travis --no-ri --no-rdoc
|
||||
RUN git clone --depth 1 https://github.com/travis-ci/travis-build ~/.travis/travis-build
|
||||
RUN bundle install --gemfile ~/.travis/travis-build/Gemfile
|
||||
|
||||
ADD . /tesseract
|
||||
WORKDIR /tesseract
|
||||
|
||||
RUN travis compile | sed -e "s/--branch\\\=\\\'\\\'/--branch=4.1/g" | bash
|
|
@ -45,7 +45,7 @@ The simplest way to compile this package is:
|
|||
`sh ./configure' instead to prevent `csh' from trying to execute
|
||||
`configure' itself.
|
||||
|
||||
Running `configure' takes awhile. While running, it prints some
|
||||
Running `configure' takes a while. While running, it prints some
|
||||
messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
@ -227,4 +227,3 @@ operates.
|
|||
|
||||
`configure' also accepts some other, not widely useful, options. Run
|
||||
`configure --help' for more details.
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
# autotools (LINUX/UNIX , msys...)
|
||||
|
||||
If you have cloned Tesseract from GitHub, you must generate
|
||||
the configure script.
|
||||
|
||||
If you have tesseract 4.0x installation in your system, please remove it
|
||||
before new build.
|
||||
|
||||
You need Leptonica 1.74.2 (minimum) for Tesseract 4.0x.
|
||||
|
||||
Known dependencies for training tools (excluding leptonica):
|
||||
* compiler with c++11 support
|
||||
* automake
|
||||
* pkg-config
|
||||
* pango-devel
|
||||
* cairo-devel
|
||||
* icu-devel
|
||||
|
||||
So, the steps for making Tesseract are:
|
||||
|
||||
$ ./autogen.sh
|
||||
$ ./configure
|
||||
$ make
|
||||
$ sudo make install
|
||||
$ sudo ldconfig
|
||||
$ make training
|
||||
$ sudo make training-install
|
||||
|
||||
You need to install at least English language and OSD traineddata files to
|
||||
`TESSDATA_PREFIX` directory.
|
||||
|
||||
You can retrieve single file with tools like [wget](https://www.gnu.org/software/wget/), [curl](https://curl.haxx.se/), [GithubDownloader](https://github.com/intezer/GithubDownloader) or browser.
|
||||
|
||||
All language data files can be retrieved from git repository (useful only for packagers!).
|
||||
(Repository is huge - more that 1.2 GB. You do NOT need to download traineddata files for
|
||||
all languages).
|
||||
|
||||
$ git clone https://github.com/tesseract-ocr/tessdata.git tesseract-ocr.tessdata
|
||||
|
||||
|
||||
You need an Internet connection and [curl](https://curl.haxx.se/) to compile `ScrollView.jar`
|
||||
because the build will automatically download
|
||||
[piccolo2d-core-3.0.jar](http://search.maven.org/remotecontent?filepath=org/piccolo2d/piccolo2d-core/3.0/piccolo2d-core-3.0.jar > piccolo2d-core-3.0.jar) and
|
||||
[piccolo2d-extras-3.0.jar](http://search.maven.org/remotecontent?filepath=org/piccolo2d/piccolo2d-extras/3.0/piccolo2d-extras-3.0.jar) and
|
||||
[jaxb-api-2.3.1.jar](http://search.maven.org/remotecontent?filepath=javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar) and place them to `tesseract/java`.
|
||||
|
||||
Just run:
|
||||
|
||||
$ make ScrollView.jar
|
||||
|
||||
and follow the instruction on [Viewer Debugging wiki](https://github.com/tesseract-ocr/tesseract/wiki/ViewerDebugging).
|
||||
|
||||
|
||||
# CMAKE
|
||||
|
||||
There is alternative build system based on multiplatform [cmake](https://cmake.org/)
|
||||
|
||||
## LINUX
|
||||
|
||||
$ mkdir build
|
||||
$ cd build && cmake .. && make
|
||||
$ sudo make install
|
||||
|
||||
|
||||
## WINDOWS
|
||||
|
||||
See [Wiki](https://github.com/tesseract-ocr/tesseract/wiki) for more information on this.
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,89 @@
|
|||
## run autogen.sh to create Makefile.in from this file
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
if ENABLE_TRAINING
|
||||
TRAINING_SUBDIR = src/training
|
||||
training: all
|
||||
@$(MAKE) -C src/training
|
||||
training-install: training
|
||||
@$(MAKE) -C src/training install
|
||||
training-uninstall:
|
||||
@$(MAKE) -C src/training uninstall
|
||||
clean-local:
|
||||
@$(MAKE) -C src/training clean
|
||||
# Some unit tests use code from training.
|
||||
check: training
|
||||
else
|
||||
training:
|
||||
@echo "Need to reconfigure project, so there are no errors"
|
||||
endif
|
||||
|
||||
.PHONY: doc install-langs ScrollView.jar install-jars training
|
||||
|
||||
SUBDIRS = src/arch src/ccutil src/viewer src/cutil src/opencl src/ccstruct
|
||||
SUBDIRS += src/dict src/classify src/wordrec src/textord src/lstm
|
||||
SUBDIRS += src/ccmain src/api . tessdata doc unittest
|
||||
|
||||
EXTRA_DIST = README.md LICENSE
|
||||
EXTRA_DIST += aclocal.m4 config configure.ac autogen.sh
|
||||
EXTRA_DIST += tesseract.pc.in $(TRAINING_SUBDIR) java doc
|
||||
EXTRA_DIST += CMakeLists.txt tesseract.pc.cmake cmake VERSION src/vs2010 cppan.yml
|
||||
|
||||
DIST_SUBDIRS = $(SUBDIRS) $(TRAINING_SUBDIR)
|
||||
|
||||
uninstall-hook:
|
||||
rm -rf $(DESTDIR)$(pkgincludedir)
|
||||
|
||||
dist-hook:
|
||||
# Need to remove .svn directories from directories
|
||||
# added using EXTRA_DIST. $(distdir)/tessdata would in
|
||||
# theory suffice.
|
||||
rm -rf `find $(distdir) -name .deps -type d`
|
||||
-rm -f $(distdir)/*/Makefile $(distdir)/*/*/Makefile
|
||||
rm -f `find $(distdir) -name '*~'`
|
||||
rm -rf $(find $(distdir)/src/training -executable -type f)
|
||||
rm -rf $(distdir)/doc/html/* $(distdir)/doc/*.log
|
||||
|
||||
ScrollView.jar:
|
||||
@cd "$(top_builddir)/java" && $(MAKE) $@
|
||||
|
||||
install-jars:
|
||||
@cd "$(top_builddir)/java" && $(MAKE) $@
|
||||
|
||||
doc:
|
||||
-srcdir="$(top_srcdir)" builddir="$(top_builddir)" \
|
||||
version="@PACKAGE_VERSION@" name="@PACKAGE_NAME@" \
|
||||
doxygen $(top_srcdir)/doc/Doxyfile
|
||||
|
||||
doc-pack: doc
|
||||
-chmod a+r $(top_builddir)/doc/html/*
|
||||
@tar --create --directory=$(top_builddir)/doc/html --verbose --file=- . | gzip -c -9 > $(top_builddir)/@PACKAGE_NAME@-@PACKAGE_VERSION@-doc-html.tar.gz;
|
||||
|
||||
doc-clean:
|
||||
rm -rf $(top_builddir)/doc/html/*
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = tesseract.pc
|
||||
|
||||
# fuzzer-api is used for fuzzing tests.
|
||||
# They are run by OSS-Fuzz https://oss-fuzz.com/, but can also be run locally.
|
||||
# Note: -fsanitize=fuzzer currently requires the clang++ compiler.
|
||||
|
||||
# LIB_FUZZING_ENGINE can be overridden by the caller.
|
||||
# This is used by OSS-Fuzz.
|
||||
LIB_FUZZING_ENGINE ?= -fsanitize=fuzzer
|
||||
|
||||
fuzzer-api: all
|
||||
fuzzer-api: $(top_srcdir)/unittest/fuzzers/fuzzer-api.cpp
|
||||
$(CXX) $(CXXFLAGS) -g $(LIB_FUZZING_ENGINE) \
|
||||
-I $(top_srcdir)/src/api \
|
||||
-I $(top_srcdir)/src/ccmain \
|
||||
-I $(top_srcdir)/src/ccstruct \
|
||||
-I $(top_srcdir)/src/ccutil \
|
||||
-I src/api \
|
||||
$< \
|
||||
src/api/.libs/libtesseract.a \
|
||||
$(LEPTONICA_LIBS) \
|
||||
$(TENSORFLOW_LIBS) \
|
||||
$(libarchive_LIBS) \
|
||||
-o $@
|
|
@ -0,0 +1,119 @@
|
|||
# Tesseract OCR
|
||||
|
||||
[![Build Status](https://travis-ci.org/tesseract-ocr/tesseract.svg?branch=master)](https://travis-ci.org/tesseract-ocr/tesseract)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/miah0ikfsf0j3819/branch/master?svg=true)](https://ci.appveyor.com/project/zdenop/tesseract/)<br>
|
||||
[![Coverity Scan Build Status](https://scan.coverity.com/projects/tesseract-ocr/badge.svg)](https://scan.coverity.com/projects/tesseract-ocr)
|
||||
[![Code Quality: Cpp](https://img.shields.io/lgtm/grade/cpp/g/tesseract-ocr/tesseract.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/tesseract-ocr/tesseract/context:cpp)
|
||||
[![Total Alerts](https://img.shields.io/lgtm/alerts/g/tesseract-ocr/tesseract.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/tesseract-ocr/tesseract/alerts)<br/>
|
||||
[![GitHub license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://raw.githubusercontent.com/tesseract-ocr/tesseract/master/LICENSE)
|
||||
[![Downloads](https://img.shields.io/badge/download-all%20releases-brightgreen.svg)](https://github.com/tesseract-ocr/tesseract/releases/)
|
||||
|
||||
## About
|
||||
|
||||
This package contains an **OCR engine** - `libtesseract` and a **command line program** - `tesseract`.
|
||||
Tesseract 4 adds a new neural net (LSTM) based OCR engine which is focused
|
||||
on line recognition, but also still supports the legacy Tesseract OCR engine of
|
||||
Tesseract 3 which works by recognizing character patterns. Compatibility with
|
||||
Tesseract 3 is enabled by using the Legacy OCR Engine mode (--oem 0).
|
||||
It also needs traineddata files which support the legacy engine, for example
|
||||
those from the tessdata repository.
|
||||
|
||||
The lead developer is Ray Smith. The maintainer is Zdenko Podobny.
|
||||
For a list of contributors see [AUTHORS](https://github.com/tesseract-ocr/tesseract/blob/main/AUTHORS)
|
||||
and GitHub's log of [contributors](https://github.com/tesseract-ocr/tesseract/graphs/contributors).
|
||||
|
||||
Tesseract has **unicode (UTF-8) support**, and can **recognize more than 100 languages** "out of the box".
|
||||
|
||||
Tesseract supports **various output formats**: plain text, ALTO, hOCR (HTML), PDF, invisible-text-only PDF, TSV.
|
||||
|
||||
You should note that in many cases, in order to get better OCR results, you'll need to **[improve the quality](https://tesseract-ocr.github.io/tessdoc/ImproveQuality) of the image** you are giving Tesseract.
|
||||
|
||||
This project **does not include a GUI application**. If you need one, please see [3rdParty](https://tesseract-ocr.github.io/tessdoc/User-Projects-%E2%80%93-3rdParty).
|
||||
|
||||
Tesseract **can be trained to recognize other languages**. See [Tesseract Training](https://tesseract-ocr.github.io/tessdoc/tess4/TrainingTesseract-4.00) for more information.
|
||||
|
||||
## Brief history
|
||||
|
||||
Tesseract was originally 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.
|
||||
In 2005 Tesseract was open sourced by HP. Since 2006 it is developed by Google.
|
||||
|
||||
The latest (LSTM based) stable version is **[4.1.2](https://github.com/tesseract-ocr/tesseract/releases/tag/4.1.2)**, released on September 18, 2021. Latest source code is available from [main branch on GitHub](https://github.com/tesseract-ocr/tesseract/tree/main). Open issues can be found in [issue tracker](https://github.com/tesseract-ocr/tesseract/issues), and on the [Planning page](https://tesseract-ocr.github.io/tessdoc/Planning).
|
||||
|
||||
The latest 3.0x version is **[3.05.02](https://github.com/tesseract-ocr/tesseract/releases/tag/3.05.02)**, released on June 19, 2018. Latest source code for 3.05 is available from [3.05 branch on GitHub](https://github.com/tesseract-ocr/tesseract/tree/3.05). There is no development for this version, but it can be used for special cases.
|
||||
|
||||
See **[Release Notes](https://tesseract-ocr.github.io/tessdoc/ReleaseNotes)** and **[Change Log](https://github.com/tesseract-ocr/tesseract/blob/4.1/ChangeLog)** for more details of the releases.
|
||||
|
||||
## Installing Tesseract
|
||||
|
||||
You can either [Install Tesseract via pre-built binary package](https://tesseract-ocr.github.io/tessdoc/) or [build it from source](https://tesseract-ocr.github.io/tessdoc/Compiling).
|
||||
|
||||
Supported Compilers are:
|
||||
|
||||
* GCC 4.8 and above
|
||||
* Clang 3.4 and above
|
||||
* MSVC 2015, 2017, 2019
|
||||
|
||||
Other compilers might work, but are not officially supported.
|
||||
|
||||
## Running Tesseract
|
||||
|
||||
Basic **[command line usage](https://tesseract-ocr.github.io/tessdoc/Command-Line-Usage)**:
|
||||
|
||||
tesseract imagename outputbase [-l lang] [--oem ocrenginemode] [--psm pagesegmode] [configfiles...]
|
||||
|
||||
For more information about the various command line options use `tesseract --help` or `man tesseract`.
|
||||
|
||||
Examples can be found in the [documentation](https://tesseract-ocr.github.io/tessdoc/Command-Line-Usage#simplest-invocation-to-ocr-an-image).
|
||||
|
||||
## For developers
|
||||
|
||||
Developers can use `libtesseract` [C](https://github.com/tesseract-ocr/tesseract/blob/main/src/api/capi.h) or [C++](https://github.com/tesseract-ocr/tesseract/blob/main/src/api/baseapi.h) API to build their own application. If you need bindings to `libtesseract` for other programming languages, please see the [wrapper section](https://tesseract-ocr.github.io/tessdoc/AddOns#tesseract-wrappers) on AddOns documentation page.
|
||||
|
||||
Documentation of Tesseract generated from source code by doxygen can be found on [tesseract-ocr.github.io](https://tesseract-ocr.github.io/).
|
||||
|
||||
## Support
|
||||
|
||||
Before you submit an issue, please review **[the guidelines for this repository](https://github.com/tesseract-ocr/tesseract/blob/main/CONTRIBUTING.md)**.
|
||||
|
||||
For support, first read the [documentation](https://tesseract-ocr.github.io/tessdoc/), particularly the [FAQ](https://tesseract-ocr.github.io/tessdoc/FAQ) to see if your problem is addressed there. If not, search the [Tesseract user forum](https://groups.google.com/d/forum/tesseract-ocr), the [Tesseract developer forum](https://groups.google.com/d/forum/tesseract-dev) and [past issues](https://github.com/tesseract-ocr/tesseract/issues), and if you still can't find what you need, ask for support in the mailing-lists.
|
||||
|
||||
Mailing-lists:
|
||||
* [tesseract-ocr](https://groups.google.com/d/forum/tesseract-ocr) - For tesseract users.
|
||||
* [tesseract-dev](https://groups.google.com/d/forum/tesseract-dev) - For tesseract developers.
|
||||
|
||||
Please report an issue only for a **bug**, not for asking questions.
|
||||
|
||||
## License
|
||||
|
||||
The code in this repository is licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
**NOTE**: This software depends on other packages that may be licensed under different open source licenses.
|
||||
|
||||
Tesseract uses [Leptonica library](http://leptonica.com/) which essentially
|
||||
uses a [BSD 2-clause license](http://leptonica.com/about-the-license.html).
|
||||
|
||||
## Dependencies
|
||||
|
||||
Tesseract uses [Leptonica library](https://github.com/DanBloomberg/leptonica)
|
||||
for opening input images (e.g. not documents like pdf).
|
||||
It is suggested to use leptonica with built-in support for [zlib](https://zlib.net),
|
||||
[png](https://sourceforge.net/projects/libpng) and
|
||||
[tiff](http://www.simplesystems.org/libtiff) (for w multipage tiff).
|
||||
|
||||
## Latest Version of README
|
||||
|
||||
For the latest online version of the README.md see:
|
||||
|
||||
https://github.com/tesseract-ocr/tesseract/blob/main/README.md
|
|
@ -1 +0,0 @@
|
|||
12
|
|
@ -1 +0,0 @@
|
|||
12
|
|
@ -1,696 +0,0 @@
|
|||
/******************************************************************************
|
||||
** 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 "blobs.h"
|
||||
#include "const.h"
|
||||
#include "mfx.h"
|
||||
#include "params.h"
|
||||
#include "classify.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MIN_INERTIA (0.00001)
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
Public Code
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
// Convert a blob into a list of MFOUTLINEs (float-based microfeature format).
|
||||
LIST ConvertBlob(TBLOB *blob) {
|
||||
LIST outlines = NIL_LIST;
|
||||
return (blob == NULL)
|
||||
? NIL_LIST
|
||||
: ConvertOutlines(blob->outlines, outlines, outer);
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
// Convert a TESSLINE into the float-based MFOUTLINE micro-feature format.
|
||||
MFOUTLINE ConvertOutline(TESSLINE *outline) {
|
||||
MFEDGEPT *NewPoint;
|
||||
MFOUTLINE MFOutline = NIL_LIST;
|
||||
EDGEPT *EdgePoint;
|
||||
EDGEPT *StartPoint;
|
||||
EDGEPT *NextPoint;
|
||||
|
||||
if (outline == NULL || outline->loop == NULL)
|
||||
return MFOutline;
|
||||
|
||||
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);
|
||||
NewPoint->Hidden = EdgePoint->IsHidden();
|
||||
NewPoint->Point.x = EdgePoint->pos.x;
|
||||
NewPoint->Point.y = EdgePoint->pos.y;
|
||||
MFOutline = push(MFOutline, NewPoint);
|
||||
}
|
||||
EdgePoint = NextPoint;
|
||||
} while (EdgePoint != StartPoint);
|
||||
|
||||
if (MFOutline != NULL)
|
||||
MakeOutlineCircular(MFOutline);
|
||||
return MFOutline;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
// Convert a tree of outlines to a list of MFOUTLINEs (lists of MFEDGEPTs).
|
||||
//
|
||||
// Parameters:
|
||||
// outline first outline to be converted
|
||||
// mf_outlines list to add converted outlines to
|
||||
// outline_type are the outlines outer or holes?
|
||||
LIST ConvertOutlines(TESSLINE *outline,
|
||||
LIST mf_outlines,
|
||||
OUTLINETYPE outline_type) {
|
||||
MFOUTLINE mf_outline;
|
||||
|
||||
while (outline != NULL) {
|
||||
mf_outline = ConvertOutline(outline);
|
||||
if (mf_outline != NULL)
|
||||
mf_outlines = push(mf_outlines, mf_outline);
|
||||
outline = outline->next;
|
||||
}
|
||||
return mf_outlines;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
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_node (Outlines);
|
||||
|
||||
Last = PointAt (Outline);
|
||||
Outline = NextPointAfter (Outline);
|
||||
EdgePoint = Outline;
|
||||
do {
|
||||
Current = PointAt (EdgePoint);
|
||||
|
||||
UpdateOutlineStats (OutlineStats,
|
||||
Last->Point.x, Last->Point.y,
|
||||
Current->Point.x, Current->Point.y);
|
||||
|
||||
Last = Current;
|
||||
EdgePoint = NextPointAfter (EdgePoint);
|
||||
}
|
||||
while (EdgePoint != Outline);
|
||||
}
|
||||
FinishOutlineStats(OutlineStats);
|
||||
|
||||
} /* ComputeOutlineStats */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
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 = list_rest (Outline);
|
||||
set_rest(Outline, NIL_LIST);
|
||||
while (Start != NULL) {
|
||||
free_struct (first_node (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 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 */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
// Return a new edge point for a micro-feature outline.
|
||||
MFEDGEPT *NewEdgePoint() {
|
||||
return ((MFEDGEPT *) alloc_struct(sizeof(MFEDGEPT), "MFEDGEPT"));
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
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 (!PointAt(EdgePoint)->ExtremityMark)
|
||||
EdgePoint = NextPointAfter(EdgePoint);
|
||||
|
||||
return (EdgePoint);
|
||||
|
||||
} /* NextExtremity */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void NormalizeOutline(MFOUTLINE Outline,
|
||||
FLOAT32 XOrigin) {
|
||||
/*
|
||||
** Parameters:
|
||||
** Outline outline to be normalized
|
||||
** 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.
|
||||
*/
|
||||
if (Outline == NIL_LIST)
|
||||
return;
|
||||
|
||||
MFOUTLINE EdgePoint = Outline;
|
||||
do {
|
||||
MFEDGEPT *Current = PointAt(EdgePoint);
|
||||
Current->Point.y = MF_SCALE_FACTOR * (Current->Point.y - BASELINE_OFFSET);
|
||||
Current->Point.x = MF_SCALE_FACTOR * (Current->Point.x - XOrigin);
|
||||
EdgePoint = NextPointAfter(EdgePoint);
|
||||
} while (EdgePoint != Outline);
|
||||
} /* NormalizeOutline */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
namespace tesseract {
|
||||
void Classify::NormalizeOutlines(LIST Outlines,
|
||||
FLOAT32 *XScale,
|
||||
FLOAT32 *YScale) {
|
||||
/*
|
||||
** Parameters:
|
||||
** Outlines list of outlines to be normalized
|
||||
** XScale x-direction scale factor used by routine
|
||||
** YScale y-direction scale factor used by routine
|
||||
** Globals:
|
||||
** classify_norm_method method being used for normalization
|
||||
** classify_char_norm_range 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 (classify_norm_method) {
|
||||
case character:
|
||||
ComputeOutlineStats(Outlines, &OutlineStats);
|
||||
|
||||
/* limit scale factor to avoid overscaling small blobs (.,`'),
|
||||
thin blobs (l1ift), and merged blobs */
|
||||
*XScale = *YScale = BaselineScale = MF_SCALE_FACTOR;
|
||||
*XScale *= OutlineStats.Ry;
|
||||
*YScale *= OutlineStats.Rx;
|
||||
if (*XScale < classify_min_norm_scale_x)
|
||||
*XScale = classify_min_norm_scale_x;
|
||||
if (*YScale < classify_min_norm_scale_y)
|
||||
*YScale = classify_min_norm_scale_y;
|
||||
if (*XScale > classify_max_norm_scale_x &&
|
||||
*YScale <= classify_max_norm_scale_y)
|
||||
*XScale = classify_max_norm_scale_x;
|
||||
*XScale = classify_char_norm_range * BaselineScale / *XScale;
|
||||
*YScale = classify_char_norm_range * BaselineScale / *YScale;
|
||||
|
||||
iterate(Outlines) {
|
||||
Outline = (MFOUTLINE) first_node (Outlines);
|
||||
CharNormalizeOutline (Outline,
|
||||
OutlineStats.x, OutlineStats.y,
|
||||
*XScale, *YScale);
|
||||
}
|
||||
break;
|
||||
|
||||
case baseline:
|
||||
iterate(Outlines) {
|
||||
Outline = (MFOUTLINE) first_node(Outlines);
|
||||
NormalizeOutline(Outline, 0.0);
|
||||
}
|
||||
*XScale = *YScale = MF_SCALE_FACTOR;
|
||||
break;
|
||||
}
|
||||
} /* NormalizeOutlines */
|
||||
} // namespace tesseract
|
||||
|
||||
/**----------------------------------------------------------------------------
|
||||
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))
|
||||
PointAt (Current)->Direction = Direction;
|
||||
|
||||
PointAt (End)->PreviousDirection = 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_LIST)
|
||||
return;
|
||||
|
||||
First = Outline;
|
||||
Current = First;
|
||||
do {
|
||||
CurrentPoint = PointAt (Current);
|
||||
CurrentPoint->Point.x =
|
||||
(CurrentPoint->Point.x - XCenter) * XScale;
|
||||
CurrentPoint->Point.y =
|
||||
(CurrentPoint->Point.y - 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 = PointAt (EdgePoint)->Direction;
|
||||
|
||||
MFOUTLINE next_pt = NULL;
|
||||
do {
|
||||
EdgePoint = NextPointAfter(EdgePoint);
|
||||
next_pt = NextPointAfter(EdgePoint);
|
||||
} while (PointAt(EdgePoint)->Direction == InitialDirection &&
|
||||
!PointAt(EdgePoint)->Hidden &&
|
||||
next_pt != NULL && !PointAt(next_pt)->Hidden);
|
||||
|
||||
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 */
|
|
@ -1,153 +0,0 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Tesseract-ocrDocumentation.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Tesseract-ocrDocumentation.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/Tesseract-ocrDocumentation"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Tesseract-ocrDocumentation"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
|
@ -1,42 +0,0 @@
|
|||
/* -*-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;
|
||||
|
||||
#endif
|
|
@ -1,61 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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
|
|
@ -1,30 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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;
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
/* -*-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.
|
||||
*
|
||||
*********************************************************************************/
|
||||
#include "matchtab.h"
|
||||
|
||||
#include "blobs.h"
|
||||
#include "callcpp.h"
|
||||
#include "elst.h"
|
||||
#include "freelist.h"
|
||||
#include "helpers.h"
|
||||
#include "ratngs.h"
|
||||
|
||||
#define NUM_MATCH_ENTRIES 500 /* Entries in match_table */
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
BlobMatchTable::BlobMatchTable()
|
||||
: been_initialized_(false), match_table_(NULL) {
|
||||
init_match_table();
|
||||
}
|
||||
|
||||
BlobMatchTable::~BlobMatchTable() {
|
||||
end_match_table();
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* init_match_table
|
||||
*
|
||||
* Create and clear a match table to be used to speed up the splitter.
|
||||
**********************************************************************/
|
||||
void BlobMatchTable::init_match_table() {
|
||||
if (been_initialized_) {
|
||||
/* Reclaim old choices */
|
||||
for (int x = 0; x < NUM_MATCH_ENTRIES; x++) {
|
||||
if (!IsEmpty(x)) {
|
||||
match_table_[x].rating->clear();
|
||||
delete match_table_[x].rating;
|
||||
// Reinitialize the entry.
|
||||
match_table_[x].box = TBOX();
|
||||
match_table_[x].rating = NULL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Allocate memory once */
|
||||
match_table_ = new MATCH[NUM_MATCH_ENTRIES];
|
||||
been_initialized_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void BlobMatchTable::end_match_table() {
|
||||
if (been_initialized_) {
|
||||
init_match_table();
|
||||
delete[] match_table_;
|
||||
match_table_ = NULL;
|
||||
been_initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* put_match
|
||||
*
|
||||
* Put a new blob and its corresponding match ratings into the match
|
||||
* table.
|
||||
**********************************************************************/
|
||||
void BlobMatchTable::put_match(TBLOB *blob, BLOB_CHOICE_LIST *ratings) {
|
||||
if (!blob) return;
|
||||
/* Hash into table */
|
||||
TBOX bbox(blob->bounding_box());
|
||||
int start = Hash(bbox);
|
||||
|
||||
/* Look for empty */
|
||||
int x = start;
|
||||
do {
|
||||
if (IsEmpty(x)) {
|
||||
/* Add this entry */
|
||||
match_table_[x].box = bbox;
|
||||
// Copy ratings to match_table_[x].rating
|
||||
match_table_[x].rating = new BLOB_CHOICE_LIST();
|
||||
match_table_[x].rating->deep_copy(ratings, &BLOB_CHOICE::deep_copy);
|
||||
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.
|
||||
**********************************************************************/
|
||||
BLOB_CHOICE_LIST *BlobMatchTable::get_match(TBLOB *blob) {
|
||||
return get_match_by_box(blob->bounding_box());
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* Hash
|
||||
*
|
||||
* The hash function we use to translate a bounding box to a starting
|
||||
* hash position in our array.
|
||||
**********************************************************************/
|
||||
int BlobMatchTable::Hash(const TBOX &box) const {
|
||||
int topleft = (box.top() << 16) + box.left();
|
||||
int botright = (box.bottom() << 16) + box.right();
|
||||
return Modulo(topleft + botright, NUM_MATCH_ENTRIES);
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* IsEmpty
|
||||
*
|
||||
* Returns whether the idx entry in the array is still empty.
|
||||
**********************************************************************/
|
||||
bool BlobMatchTable::IsEmpty(int idx) const {
|
||||
return TBOX() == match_table_[idx].box &&
|
||||
NULL == match_table_[idx].rating;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* get_match_by_box
|
||||
*
|
||||
* 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.
|
||||
**********************************************************************/
|
||||
BLOB_CHOICE_LIST *BlobMatchTable::get_match_by_box(const TBOX &box) {
|
||||
int start = Hash(box);
|
||||
int x = start;
|
||||
/* Search for match */
|
||||
do {
|
||||
/* Not found when blank */
|
||||
if (IsEmpty(x))
|
||||
break;
|
||||
/* Is this the match ? */
|
||||
if (match_table_[x].box == box) {
|
||||
BLOB_CHOICE_LIST *blist = new BLOB_CHOICE_LIST();
|
||||
blist->deep_copy(match_table_[x].rating, &BLOB_CHOICE::deep_copy);
|
||||
return blist;
|
||||
}
|
||||
if (++x >= NUM_MATCH_ENTRIES)
|
||||
x = 0;
|
||||
} while (x != start);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* add_to_match
|
||||
*
|
||||
* Update ratings list in the match_table corresponding to the given
|
||||
* blob. The function assumes that:
|
||||
* -- the match table contains the initial non-NULL list with choices
|
||||
* for the given blob
|
||||
* -- the new ratings list is a superset of the corresponding list in
|
||||
* the match_table and the unichar ids of the blob choices in the
|
||||
* list are unique.
|
||||
* The entries that appear in the new ratings list and not in the
|
||||
* old one are added to the old ratings list in the match_table.
|
||||
**********************************************************************/
|
||||
void BlobMatchTable::add_to_match(TBLOB *blob, BLOB_CHOICE_LIST *ratings) {
|
||||
TBOX bbox = blob->bounding_box();
|
||||
int start = Hash(bbox);
|
||||
int x = start;
|
||||
do {
|
||||
if (IsEmpty(x)) {
|
||||
fprintf(stderr, "Can not update uninitialized entry in match_table\n");
|
||||
ASSERT_HOST(!IsEmpty(x));
|
||||
}
|
||||
if (match_table_[x].box == bbox) {
|
||||
// Copy new ratings to match_table_[x].rating.
|
||||
BLOB_CHOICE_IT it;
|
||||
it.set_to_list(match_table_[x].rating);
|
||||
BLOB_CHOICE_IT new_it;
|
||||
new_it.set_to_list(ratings);
|
||||
assert(it.length() <= new_it.length());
|
||||
for (it.mark_cycle_pt(), new_it.mark_cycle_pt();
|
||||
!it.cycled_list() && !new_it.cycled_list(); new_it.forward()) {
|
||||
if (it.data()->unichar_id() == new_it.data()->unichar_id()) {
|
||||
it.forward();
|
||||
} else {
|
||||
it.add_before_stay_put(new BLOB_CHOICE(*(new_it.data())));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (++x >= NUM_MATCH_ENTRIES)
|
||||
x = 0;
|
||||
} while (x != start);
|
||||
}
|
||||
|
||||
} // namespace tesseract
|
|
@ -1,310 +0,0 @@
|
|||
/******************************************************************************
|
||||
** 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
|
||||
-----------------------------------------------------------------------------*/
|
||||
#ifdef _MSC_VER
|
||||
#include "mathfix.h"
|
||||
#endif
|
||||
|
||||
#include "featdefs.h"
|
||||
#include "emalloc.h"
|
||||
#include "danerror.h"
|
||||
#include "scanutils.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/** define errors triggered by this module */
|
||||
#define ILLEGAL_NUM_SETS 3001
|
||||
|
||||
#define PICO_FEATURE_LENGTH 0.05
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
Global Data Definitions and Declarations
|
||||
-----------------------------------------------------------------------------*/
|
||||
const char* kMicroFeatureType = "mf";
|
||||
const char* kCNFeatureType = "cn";
|
||||
const char* kIntFeatureType = "if";
|
||||
const char* kGeoFeatureType = "tb";
|
||||
|
||||
// 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, 1, 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 parameters).
|
||||
DefineFeature(MicroFeatureDesc, 5, 1, kMicroFeatureType, MicroFeatureParams)
|
||||
|
||||
// Define all of the parameters for the NormFeat type.
|
||||
StartParamDesc (CharNormParams)
|
||||
DefineParam(0, 0, -0.25, 0.75)
|
||||
DefineParam(0, 1, 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 parameters).
|
||||
DefineFeature(CharNormDesc, 4, 0, kCNFeatureType, CharNormParams)
|
||||
|
||||
// Define all of the parameters for the IntFeature type
|
||||
StartParamDesc(IntFeatParams)
|
||||
DefineParam(0, 0, 0.0, 255.0)
|
||||
DefineParam(0, 0, 0.0, 255.0)
|
||||
DefineParam(1, 0, 0.0, 255.0)
|
||||
EndParamDesc
|
||||
// Now define the feature type itself (see features.h for parameters).
|
||||
DefineFeature(IntFeatDesc, 2, 1, kIntFeatureType, IntFeatParams)
|
||||
|
||||
// Define all of the parameters for the GeoFeature type
|
||||
StartParamDesc(GeoFeatParams)
|
||||
DefineParam(0, 0, 0.0, 255.0)
|
||||
DefineParam(0, 0, 0.0, 255.0)
|
||||
DefineParam(0, 0, 0.0, 255.0)
|
||||
EndParamDesc
|
||||
// Now define the feature type itself (see features.h for parameters).
|
||||
DefineFeature(GeoFeatDesc, 3, 0, kGeoFeatureType, GeoFeatParams)
|
||||
|
||||
// Other features used for training the adaptive classifier, but not used
|
||||
// during normal training, therefore not in the DescDefs array.
|
||||
|
||||
// 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 parameters).
|
||||
DefineFeature(PicoFeatDesc, 2, 1, "pf", PicoFeatParams)
|
||||
|
||||
// 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 parameters).
|
||||
DefineFeature(OutlineFeatDesc, 3, 1, "of", OutlineFeatParams)
|
||||
|
||||
// MUST be kept in-sync with ExtractorDefs in fxdefs.cpp.
|
||||
static const FEATURE_DESC_STRUCT *DescDefs[NUM_FEATURE_TYPES] = {
|
||||
&MicroFeatureDesc,
|
||||
&CharNormDesc,
|
||||
&IntFeatDesc,
|
||||
&GeoFeatDesc
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
Public Code
|
||||
-----------------------------------------------------------------------------*/
|
||||
void InitFeatureDefs(FEATURE_DEFS_STRUCT *featuredefs) {
|
||||
featuredefs->NumFeatureTypes = NUM_FEATURE_TYPES;
|
||||
for (int i = 0; i < NUM_FEATURE_TYPES; ++i) {
|
||||
featuredefs->FeatureDesc[i] = DescDefs[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Release the memory consumed by the specified character
|
||||
* description and all of the features in that description.
|
||||
*
|
||||
* @param CharDesc character description to be deallocated
|
||||
*
|
||||
* Globals:
|
||||
* - none
|
||||
*
|
||||
* @note Exceptions: none
|
||||
* @note History: Wed May 23 13:52:19 1990, DSJ, Created.
|
||||
*/
|
||||
void FreeCharDescription(CHAR_DESC CharDesc) {
|
||||
int i;
|
||||
|
||||
if (CharDesc) {
|
||||
for (i = 0; i < CharDesc->NumFeatureSets; i++)
|
||||
FreeFeatureSet (CharDesc->FeatureSets[i]);
|
||||
Efree(CharDesc);
|
||||
}
|
||||
} /* FreeCharDescription */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Allocate a new character description, initialize its
|
||||
* feature sets to be empty, and return it.
|
||||
*
|
||||
* Globals:
|
||||
* - none
|
||||
*
|
||||
* @return New character description structure.
|
||||
* @note Exceptions: none
|
||||
* @note History: Wed May 23 15:27:10 1990, DSJ, Created.
|
||||
*/
|
||||
CHAR_DESC NewCharDescription(const FEATURE_DEFS_STRUCT &FeatureDefs) {
|
||||
CHAR_DESC CharDesc;
|
||||
int i;
|
||||
|
||||
CharDesc = (CHAR_DESC) Emalloc (sizeof (CHAR_DESC_STRUCT));
|
||||
CharDesc->NumFeatureSets = FeatureDefs.NumFeatureTypes;
|
||||
|
||||
for (i = 0; i < CharDesc->NumFeatureSets; i++)
|
||||
CharDesc->FeatureSets[i] = NULL;
|
||||
|
||||
return (CharDesc);
|
||||
|
||||
} /* NewCharDescription */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Globals:
|
||||
* - none
|
||||
*
|
||||
* @param FeatureDefs definitions of feature types/extractors
|
||||
* @param File open text file to write CharDesc to
|
||||
* @param CharDesc character description to write to File
|
||||
*
|
||||
* @note Exceptions: none
|
||||
* @note History: Wed May 23 17:21:18 1990, DSJ, Created.
|
||||
*/
|
||||
void WriteCharDescription(const FEATURE_DEFS_STRUCT &FeatureDefs,
|
||||
FILE *File, CHAR_DESC CharDesc) {
|
||||
int Type;
|
||||
int NumSetsToWrite = 0;
|
||||
|
||||
for (Type = 0; Type < CharDesc->NumFeatureSets; Type++)
|
||||
if (CharDesc->FeatureSets[Type])
|
||||
NumSetsToWrite++;
|
||||
|
||||
fprintf (File, " %d\n", NumSetsToWrite);
|
||||
for (Type = 0; Type < CharDesc->NumFeatureSets; Type++)
|
||||
if (CharDesc->FeatureSets[Type]) {
|
||||
fprintf (File, "%s ", (FeatureDefs.FeatureDesc[Type])->ShortName);
|
||||
WriteFeatureSet (File, CharDesc->FeatureSets[Type]);
|
||||
}
|
||||
} /* WriteCharDescription */
|
||||
|
||||
// Return whether all of the fields of the given feature set
|
||||
// are well defined (not inf or nan).
|
||||
bool ValidCharDescription(const FEATURE_DEFS_STRUCT &FeatureDefs,
|
||||
CHAR_DESC CharDesc) {
|
||||
bool anything_written = false;
|
||||
bool well_formed = true;
|
||||
for (int Type = 0; Type < CharDesc->NumFeatureSets; Type++) {
|
||||
if (CharDesc->FeatureSets[Type]) {
|
||||
for (int i = 0; i < CharDesc->FeatureSets[Type]->NumFeatures; i++) {
|
||||
FEATURE feat = CharDesc->FeatureSets[Type]->Features[i];
|
||||
for (int p = 0; p < feat->Type->NumParams; p++) {
|
||||
if (isnan(feat->Params[p]) || isinf(feat->Params[p]))
|
||||
well_formed = false;
|
||||
else
|
||||
anything_written = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return anything_written && well_formed;
|
||||
} /* ValidCharDescription */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/**
|
||||
* Read a character description from File, and return
|
||||
* a data structure containing this information. The data
|
||||
* is formatted as follows:
|
||||
* @verbatim
|
||||
NumberOfSets
|
||||
ShortNameForSet1 Set1
|
||||
ShortNameForSet2 Set2
|
||||
...
|
||||
@endverbatim
|
||||
*
|
||||
* Globals:
|
||||
* - none
|
||||
*
|
||||
* @param FeatureDefs definitions of feature types/extractors
|
||||
* @param File open text file to read character description from
|
||||
* @return Character description read from File.
|
||||
* @note Exceptions:
|
||||
* - ILLEGAL_NUM_SETS
|
||||
* @note History: Wed May 23 17:32:48 1990, DSJ, Created.
|
||||
*/
|
||||
CHAR_DESC ReadCharDescription(const FEATURE_DEFS_STRUCT &FeatureDefs,
|
||||
FILE *File) {
|
||||
int NumSetsToRead;
|
||||
char ShortName[FEAT_NAME_SIZE];
|
||||
CHAR_DESC CharDesc;
|
||||
int Type;
|
||||
|
||||
if (fscanf (File, "%d", &NumSetsToRead) != 1 ||
|
||||
NumSetsToRead < 0 || NumSetsToRead > FeatureDefs.NumFeatureTypes)
|
||||
DoError (ILLEGAL_NUM_SETS, "Illegal number of feature sets");
|
||||
|
||||
CharDesc = NewCharDescription(FeatureDefs);
|
||||
for (; NumSetsToRead > 0; NumSetsToRead--) {
|
||||
fscanf (File, "%s", ShortName);
|
||||
Type = ShortNameToFeatureType(FeatureDefs, ShortName);
|
||||
CharDesc->FeatureSets[Type] =
|
||||
ReadFeatureSet (File, FeatureDefs.FeatureDesc[Type]);
|
||||
}
|
||||
return (CharDesc);
|
||||
|
||||
} // ReadCharDescription
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Globals:
|
||||
* - none
|
||||
*
|
||||
* @param FeatureDefs definitions of feature types/extractors
|
||||
* @param ShortName short name of a feature type
|
||||
* @return Feature type which corresponds to ShortName.
|
||||
* @note Exceptions:
|
||||
* - ILLEGAL_SHORT_NAME
|
||||
* @note History: Wed May 23 15:36:05 1990, DSJ, Created.
|
||||
*/
|
||||
int ShortNameToFeatureType(const FEATURE_DEFS_STRUCT &FeatureDefs,
|
||||
const char *ShortName) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < FeatureDefs.NumFeatureTypes; i++)
|
||||
if (!strcmp ((FeatureDefs.FeatureDesc[i]->ShortName), ShortName))
|
||||
return (i);
|
||||
DoError (ILLEGAL_SHORT_NAME, "Illegal short name for a feature");
|
||||
return 0;
|
||||
|
||||
} // ShortNameToFeatureType
|
|
@ -1,905 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////
|
||||
// File: dict.h
|
||||
// Description: dict class.
|
||||
// Author: Samuel Charron
|
||||
//
|
||||
// (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 TESSERACT_DICT_DICT_H_
|
||||
#define TESSERACT_DICT_DICT_H_
|
||||
|
||||
#include "ambigs.h"
|
||||
#include "dawg.h"
|
||||
#include "host.h"
|
||||
#include "image.h"
|
||||
#include "oldlist.h"
|
||||
#include "ratngs.h"
|
||||
#include "stopper.h"
|
||||
#include "trie.h"
|
||||
#include "unicharset.h"
|
||||
#include "permute.h"
|
||||
|
||||
#define MAX_WERD_LENGTH (inT64) 128
|
||||
#define NO_RATING -1
|
||||
|
||||
/** Struct used to hold temporary information about fragments. */
|
||||
struct CHAR_FRAGMENT_INFO {
|
||||
UNICHAR_ID unichar_id;
|
||||
const CHAR_FRAGMENT *fragment;
|
||||
int num_fragments;
|
||||
float rating;
|
||||
float certainty;
|
||||
};
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
typedef GenericVector<Dawg *> DawgVector;
|
||||
|
||||
//
|
||||
// Constants
|
||||
//
|
||||
static const int kAnyWordLength = -1;
|
||||
static const int kRatingPad = 4;
|
||||
// TODO(daria): If hyphens are different in different languages and can be
|
||||
// inferred from training data we should load their values dynamically.
|
||||
static const char kHyphenSymbol[] = "-";
|
||||
static const int kMaxNumDawgEdgees = 2000000;
|
||||
static const int kMaxDocDawgEdges = 250000;
|
||||
static const int kMaxUserDawgEdges = 50000;
|
||||
static const float kSimCertaintyScale = -10.0; // similarity matcher scaling
|
||||
static const float kSimCertaintyOffset = -10.0; // similarity matcher offset
|
||||
static const float kSimilarityFloor = 100.0; // worst E*L product to stop on
|
||||
static const int kDocDictMaxRepChars = 4;
|
||||
|
||||
struct DawgArgs {
|
||||
DawgArgs(DawgInfoVector *d, DawgInfoVector *c, DawgInfoVector *ud,
|
||||
DawgInfoVector *uc, float r, PermuterType p, int len, int e) :
|
||||
active_dawgs(d), constraints(c), updated_active_dawgs(ud),
|
||||
updated_constraints(uc), rating_margin(r) {
|
||||
for (int i = 0; i < MAX_WERD_LENGTH; ++i) {
|
||||
rating_array[i] = NO_RATING;
|
||||
}
|
||||
permuter = p;
|
||||
sought_word_length = len;
|
||||
end_char_choice_index = e;
|
||||
}
|
||||
DawgInfoVector *active_dawgs;
|
||||
DawgInfoVector *constraints;
|
||||
DawgInfoVector *updated_active_dawgs;
|
||||
DawgInfoVector *updated_constraints;
|
||||
PermuterType permuter;
|
||||
int sought_word_length;
|
||||
|
||||
// TODO(daria): remove these fields when permdawg is deprecated.
|
||||
float rating_margin; /**< pruning margin ratio */
|
||||
float rating_array[MAX_WERD_LENGTH];
|
||||
int end_char_choice_index;
|
||||
};
|
||||
|
||||
class Dict {
|
||||
public:
|
||||
Dict(Image* image_ptr);
|
||||
~Dict();
|
||||
const Image* getImage() const {
|
||||
return image_ptr_;
|
||||
}
|
||||
Image* getImage() {
|
||||
return image_ptr_;
|
||||
}
|
||||
const UNICHARSET& getUnicharset() const {
|
||||
return getImage()->getCCUtil()->unicharset;
|
||||
}
|
||||
UNICHARSET& getUnicharset() {
|
||||
return getImage()->getCCUtil()->unicharset;
|
||||
}
|
||||
const UnicharAmbigs &getUnicharAmbigs() {
|
||||
return getImage()->getCCUtil()->unichar_ambigs;
|
||||
}
|
||||
|
||||
inline bool compound_marker(UNICHAR_ID unichar_id) {
|
||||
return (unichar_id == getUnicharset().unichar_to_id("-") ||
|
||||
unichar_id == getUnicharset().unichar_to_id("/"));
|
||||
}
|
||||
|
||||
/* hyphen.cpp ************************************************************/
|
||||
|
||||
/// Returns true if we've recorded the beginning of a hyphenated word.
|
||||
inline bool hyphenated() const { return
|
||||
!last_word_on_line_ && hyphen_word_ && GetMaxFixedLengthDawgIndex() < 0;
|
||||
}
|
||||
/// Size of the base word (the part on the line before) of a hyphenated word.
|
||||
inline int hyphen_base_size() const {
|
||||
return this->hyphenated() ? hyphen_word_->length() : 0;
|
||||
}
|
||||
/// If this word is hyphenated copy the base word (the part on
|
||||
/// the line before) of a hyphenated word into the given word.
|
||||
/// This function assumes that word is not NULL.
|
||||
inline void copy_hyphen_info(WERD_CHOICE *word) const {
|
||||
if (this->hyphenated()) {
|
||||
*word = *hyphen_word_;
|
||||
if (hyphen_debug_level) word->print("copy_hyphen_info: ");
|
||||
}
|
||||
}
|
||||
/// Erase the unichar ids corresponding to the portion of the word
|
||||
/// from the previous line. The word is not changed if it is not
|
||||
/// split between lines and hyphenated.
|
||||
inline void remove_hyphen_head(WERD_CHOICE *word) const {
|
||||
if (this->hyphenated()) {
|
||||
word->remove_unichar_ids(0, hyphen_word_->length());
|
||||
if (hyphen_debug_level) hyphen_word_->print("remove_hyphen_head: ");
|
||||
}
|
||||
}
|
||||
/// Check whether the word has a hyphen at the end.
|
||||
inline bool has_hyphen_end(UNICHAR_ID unichar_id, bool first_pos) const {
|
||||
return (last_word_on_line_ && !first_pos &&
|
||||
unichar_id == hyphen_unichar_id_);
|
||||
}
|
||||
/// Same as above, but check the unichar at the end of the word.
|
||||
inline bool has_hyphen_end(const WERD_CHOICE &word) const {
|
||||
int word_index = word.length() - 1;
|
||||
return has_hyphen_end(word.unichar_id(word_index), word_index == 0);
|
||||
}
|
||||
/// Unless the previous word was the last one on the line, and the current
|
||||
/// one is not (thus it is the first one on the line), erase hyphen_word_,
|
||||
/// clear hyphen_active_dawgs_, hyphen_constraints_ update last_word_on_line_.
|
||||
void reset_hyphen_vars(bool last_word_on_line);
|
||||
/// Update hyphen_word_, and copy the given DawgInfoVectors into
|
||||
/// hyphen_active_dawgs_ and hyphen_constraints_.
|
||||
void set_hyphen_word(const WERD_CHOICE &word,
|
||||
const DawgInfoVector &active_dawgs,
|
||||
const DawgInfoVector &constraints);
|
||||
|
||||
/* permdawg.cpp ************************************************************/
|
||||
/// Copies word into best_choice if its rating is smaller
|
||||
/// than that of best_choice.
|
||||
inline void update_best_choice(const WERD_CHOICE &word,
|
||||
WERD_CHOICE *best_choice) {
|
||||
if (word.rating() < best_choice->rating()) *best_choice = word;
|
||||
}
|
||||
/// Fill the given active_dawgs vector with dawgs that could contain the
|
||||
/// beginning of the word. If hyphenated() returns true, copy the entries
|
||||
/// from hyphen_active_dawgs_ instead.
|
||||
void init_active_dawgs(int sought_word_length,
|
||||
DawgInfoVector *active_dawgs,
|
||||
bool ambigs_mode) const;
|
||||
/// If hyphenated() returns true, copy the entries from hyphen_constraints_
|
||||
/// into the given constraints vector.
|
||||
void init_constraints(DawgInfoVector *constraints) const;
|
||||
/// Returns true if we are operating in ambigs mode.
|
||||
inline bool ambigs_mode(float rating_limit) {
|
||||
return rating_limit <= 0.0;
|
||||
}
|
||||
/// Recursively explore all the possible character combinations in
|
||||
/// the given char_choices. Use go_deeper_dawg_fxn() to explore all the
|
||||
/// dawgs in the dawgs_ vector in parallel and discard invalid words.
|
||||
///
|
||||
/// Allocate and return a WERD_CHOICE with the best valid word found.
|
||||
WERD_CHOICE *dawg_permute_and_select(
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices, float rating_limit,
|
||||
int sought_word_length, int end_char_choice_index);
|
||||
WERD_CHOICE *dawg_permute_and_select(
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices, float rating_limit) {
|
||||
return dawg_permute_and_select(char_choices, rating_limit,
|
||||
kAnyWordLength, 0);
|
||||
}
|
||||
/// If the choice being composed so far could be a dictionary word
|
||||
/// and we have not reached the end of the word keep exploring the
|
||||
/// char_choices further.
|
||||
/// Also:
|
||||
/// -- sets hyphen word if needed
|
||||
/// -- if word_ending is true and the word is better than best_choice,
|
||||
/// copies word to best_choice and logs new word choice
|
||||
void go_deeper_dawg_fxn(
|
||||
const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
int char_choice_index, const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
||||
bool word_ending, WERD_CHOICE *word, float certainties[],
|
||||
float *limit, WERD_CHOICE *best_choice, int *attempts_left,
|
||||
void *void_more_args);
|
||||
|
||||
/* permute.cpp *************************************************************/
|
||||
WERD_CHOICE *get_top_choice_word(
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices);
|
||||
WERD_CHOICE *permute_top_choice(
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
float* rating_limit,
|
||||
WERD_CHOICE *raw_choice,
|
||||
BOOL8 *any_alpha);
|
||||
const char* choose_il1(const char *first_char, //first choice
|
||||
const char *second_char, //second choice
|
||||
const char *third_char, //third choice
|
||||
const char *prev_char, //prev in word
|
||||
const char *next_char, //next in word
|
||||
const char *next_next_char); //after next next in word
|
||||
WERD_CHOICE *permute_all(const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
const WERD_CHOICE *best_choice,
|
||||
WERD_CHOICE *raw_choice);
|
||||
void end_permute();
|
||||
void permute_subword(const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
float rating_limit,
|
||||
int start,
|
||||
int end,
|
||||
WERD_CHOICE *current_word);
|
||||
bool permute_characters(const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
WERD_CHOICE *best_choice,
|
||||
WERD_CHOICE *raw_choice);
|
||||
WERD_CHOICE *permute_compound_words(
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
float rating_limit);
|
||||
/// Find permutations matching a list of fixed-char-length dawgs
|
||||
/// The bestchoice based on this permuter alone is returned. Alternatively,
|
||||
/// non-conflicting changes can be combined through permuter_state.
|
||||
WERD_CHOICE *permute_fixed_length_words(
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
PermuterState *permuter_state);
|
||||
/// Incoporate segmentation cost into word rating
|
||||
void incorporate_segcost(WERD_CHOICE* word);
|
||||
/// Checks for script-consistent permutations. Similar to fixed-length
|
||||
/// permuter, the best choice is returned by the function, but the combined
|
||||
/// changes are also recorded into permuter_state.
|
||||
WERD_CHOICE *permute_script_words(
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
PermuterState *permuter_state);
|
||||
/// checks for consistency in character property (eg. alpah, digit, punct)
|
||||
WERD_CHOICE *permute_chartype_words(
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
PermuterState *permuter_state);
|
||||
|
||||
/// Look up the main chartype for each character position and store it in
|
||||
/// the given array. Also returns the dominant type from unambiguous top
|
||||
/// choices.
|
||||
char top_word_chartype(const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
char* pos_chartypes);
|
||||
|
||||
WERD_CHOICE *top_fragments_permute_and_select(
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
float rating_limit);
|
||||
/// While the choice being composed so far could be better
|
||||
/// than best_choice keeps exploring char_choices.
|
||||
/// If the end of the word is reached and the word is better than
|
||||
/// best_choice, copies word to best_choice and logs the new word choice.
|
||||
void go_deeper_top_fragments_fxn(
|
||||
const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
int char_choice_index, const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
||||
bool word_ending, WERD_CHOICE *word, float certainties[], float *limit,
|
||||
WERD_CHOICE *best_choice, int *attempts_left, void *more_args);
|
||||
|
||||
/// Semi-generic functions used by multiple permuters.
|
||||
bool fragment_state_okay(UNICHAR_ID curr_unichar_id,
|
||||
float curr_rating, float curr_certainty,
|
||||
const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
||||
const char *debug, int word_ending,
|
||||
CHAR_FRAGMENT_INFO *char_frag_info);
|
||||
void permute_choices(
|
||||
const char *debug,
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
int char_choice_index,
|
||||
const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
||||
WERD_CHOICE *word,
|
||||
float certainties[],
|
||||
float *limit,
|
||||
WERD_CHOICE *best_choice,
|
||||
int *attempts_left,
|
||||
void *more_args);
|
||||
|
||||
void append_choices(
|
||||
const char *debug,
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
const BLOB_CHOICE &blob_choice,
|
||||
int char_choice_index,
|
||||
const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
||||
WERD_CHOICE *word,
|
||||
float certainties[],
|
||||
float *limit,
|
||||
WERD_CHOICE *best_choice,
|
||||
int *attempts_left,
|
||||
void *more_args);
|
||||
/// Pointer to go_deeper function that will be modified by various permuters.
|
||||
void (Dict::*go_deeper_fxn_)(const char *debug,
|
||||
const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
int char_choice_index,
|
||||
const CHAR_FRAGMENT_INFO *prev_char_frag_info,
|
||||
bool word_ending, WERD_CHOICE *word,
|
||||
float certainties[], float *limit,
|
||||
WERD_CHOICE *best_choice, int *attempts_left,
|
||||
void *void_more_args);
|
||||
/* stopper.cpp *************************************************************/
|
||||
bool NoDangerousAmbig(WERD_CHOICE *BestChoice,
|
||||
DANGERR *fixpt,
|
||||
bool fix_replaceable,
|
||||
BLOB_CHOICE_LIST_VECTOR *Choices,
|
||||
bool *modified_blobs);
|
||||
double StopperAmbigThreshold(double f1, double f2) {
|
||||
return (f2 - f1) * stopper_ambiguity_threshold_gain -
|
||||
stopper_ambiguity_threshold_offset;
|
||||
}
|
||||
// If the certainty of any chunk in Choice (item1) is not ambiguous with the
|
||||
// corresponding chunk in the best choice (item2), frees Choice and
|
||||
// returns true.
|
||||
int FreeBadChoice(void *item1, // VIABLE_CHOICE Choice
|
||||
void *item2); // EXPANDED_CHOICE *BestChoice
|
||||
/// Replaces the corresponding wrong ngram in werd_choice with the correct
|
||||
/// one. We indicate that this newly inserted ngram unichar is composed from
|
||||
/// several fragments and modify the corresponding entries in blob_choices to
|
||||
/// contain fragments of the correct ngram unichar instead of the original
|
||||
/// unichars. Ratings and certainties of entries in blob_choices and
|
||||
/// werd_choice are unichaged. E.g. for werd_choice mystring'' and ambiguity
|
||||
/// ''->": werd_choice becomes mystring", first ' in blob_choices becomes
|
||||
/// |"|0|2, second one is set to |"|1|2.
|
||||
void ReplaceAmbig(int wrong_ngram_begin_index, int wrong_ngram_size,
|
||||
UNICHAR_ID correct_ngram_id, WERD_CHOICE *werd_choice,
|
||||
BLOB_CHOICE_LIST_VECTOR *blob_choices,
|
||||
bool *modified_blobs);
|
||||
|
||||
inline void DisableChoiceAccum() { keep_word_choices_ = false; }
|
||||
inline void EnableChoiceAccum() { keep_word_choices_ = true; }
|
||||
inline bool ChoiceAccumEnabled() { return keep_word_choices_; }
|
||||
|
||||
/// Returns the length of the shortest alpha run in WordChoice.
|
||||
int LengthOfShortestAlphaRun(const WERD_CHOICE &WordChoice);
|
||||
/// Allocates a new viable choice data structure, copies WordChoice,
|
||||
/// Certainties, and current_segmentation_ into it, returns a pointer to
|
||||
/// the newly created VIABLE_CHOICE.
|
||||
/// WordChoice is a choice to be converted to a viable choice.
|
||||
/// AdjustFactor is a factor used to adjust ratings for WordChoice.
|
||||
/// Certainties contain certainty for each character in WordChoice.
|
||||
VIABLE_CHOICE NewViableChoice(const WERD_CHOICE &WordChoice,
|
||||
FLOAT32 AdjustFactor,
|
||||
const float Certainties[]);
|
||||
/// Dumps a text representation of the specified Choice to File.
|
||||
void PrintViableChoice(FILE *File, const char *Label, VIABLE_CHOICE Choice);
|
||||
/// Compares unichar ids in word_choice to those in viable_choice,
|
||||
/// returns true if they are the same.
|
||||
bool StringSameAs(const WERD_CHOICE &WordChoice,
|
||||
VIABLE_CHOICE ViableChoice);
|
||||
/// Compares String to ViableChoice and returns true if they are the same.
|
||||
bool StringSameAs(const char *String,
|
||||
const char *String_lengths,
|
||||
VIABLE_CHOICE ViableChoice);
|
||||
/// 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.
|
||||
int UniformCertainties(const BLOB_CHOICE_LIST_VECTOR &Choices,
|
||||
const WERD_CHOICE &BestChoice);
|
||||
/// Returns true if the given best_choice is good enough to stop.
|
||||
bool AcceptableChoice(BLOB_CHOICE_LIST_VECTOR *Choices,
|
||||
WERD_CHOICE *BestChoice,
|
||||
DANGERR *fixpt,
|
||||
ACCEPTABLE_CHOICE_CALLER caller,
|
||||
bool *modified_blobs);
|
||||
/// Returns 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.
|
||||
bool AcceptableResult(const WERD_CHOICE &BestChoice);
|
||||
/// Compares the corresponding strings of WordChoice and ViableChoice and
|
||||
/// returns true if they are the same.
|
||||
int ChoiceSameAs(const WERD_CHOICE &WordChoice, VIABLE_CHOICE ViableChoice);
|
||||
/// Adds Choice to ChoicesList if the adjusted certainty for Choice is within
|
||||
/// a reasonable range of the best choice in ChoicesList. The ChoicesList list
|
||||
/// is kept in sorted order by rating. Duplicates are removed.
|
||||
/// WordChoice is the new choice for current word.
|
||||
/// AdjustFactor is an adjustment factor which was applied to choice.
|
||||
/// Certainties are certainties for each char in new choice.
|
||||
/// raw_choice indicates whether WordChoice is a raw or best choice.
|
||||
void LogNewChoice(FLOAT32 AdjustFactor, const float Certainties[],
|
||||
bool raw_choice, WERD_CHOICE *WordChoice);
|
||||
void EndDangerousAmbigs();
|
||||
/// Returns true if WordChoice is the same as the current best choice.
|
||||
bool CurrentBestChoiceIs(const WERD_CHOICE &WordChoice);
|
||||
/// Returns the adjustment factor for the best choice for the current word.
|
||||
FLOAT32 CurrentBestChoiceAdjustFactor();
|
||||
/// Returns true if there are multiple good choices for the current word.
|
||||
bool CurrentWordAmbig();
|
||||
/// Prints the current choices for this word to stdout.
|
||||
void DebugWordChoices();
|
||||
/// Print all the choices in raw_choices_ list for non 1-1 ambiguities.
|
||||
void PrintAmbigAlternatives(FILE *file, const char *label,
|
||||
int label_num_unichars);
|
||||
/// Fill ViableChoice with information from WordChoice, AChoice, AdjustFactor,
|
||||
/// and Certainties.
|
||||
void FillViableChoice(const WERD_CHOICE &WordChoice,
|
||||
FLOAT32 AdjustFactor, const float Certainties[],
|
||||
VIABLE_CHOICE ViableChoice);
|
||||
/// Returns true if there are no alternative choices for the current word
|
||||
/// or if all alternatives have an adjust factor worse than Threshold.
|
||||
bool AlternativeChoicesWorseThan(FLOAT32 Threshold);
|
||||
/// Removes from best_choices_ all choices which are not within a reasonable
|
||||
/// range of the best choice.
|
||||
void FilterWordChoices();
|
||||
/// Compares the best choice for the current word to the best raw choice
|
||||
/// to determine which characters were classified incorrectly by the
|
||||
/// classifier. 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.
|
||||
/// MinRating limits how tight to make a template.
|
||||
/// MaxRating limits how loose to make a template.
|
||||
/// RatingMargin denotes the amount of margin to put in template.
|
||||
void FindClassifierErrors(FLOAT32 MinRating,
|
||||
FLOAT32 MaxRating,
|
||||
FLOAT32 RatingMargin,
|
||||
FLOAT32 Thresholds[]);
|
||||
/// Initializes the data structures used to keep track the good word choices
|
||||
/// found for a word.
|
||||
void InitChoiceAccum();
|
||||
/// Clears best_choices_ list accumulated by the stopper.
|
||||
void ClearBestChoiceAccum();
|
||||
/// Updates the blob widths in current_segmentation_ to be the same as
|
||||
/// provided in BlobWidth. BlobWidth[] contains the number of chunks in each
|
||||
/// blob in the current segmentation.
|
||||
void LogNewSegmentation(PIECES_STATE BlobWidth);
|
||||
/// Given Blob (the index of the blob that was split), adds 1 chunk to the
|
||||
/// specified blob for each choice in best_choices_ and for best_raw_choice_.
|
||||
void LogNewSplit(int Blob);
|
||||
/// Increments the chunk count of the character in Choice which corresponds
|
||||
/// to Blob (index of the blob being split).
|
||||
void AddNewChunk(VIABLE_CHOICE Choice, int Blob);
|
||||
/// Sets up stopper variables in preparation for the first pass.
|
||||
void SettupStopperPass1();
|
||||
/// Sets up stopper variables in preparation for the second pass.
|
||||
void SettupStopperPass2();
|
||||
/* context.cpp *************************************************************/
|
||||
/// Check a string to see if it matches a set of lexical rules.
|
||||
int case_ok(const WERD_CHOICE &word, const UNICHARSET &unicharset);
|
||||
/// Returns true if the word looks like an absolute garbage
|
||||
/// (e.g. image mistakenly recognized as text).
|
||||
bool absolute_garbage(const WERD_CHOICE &word, const UNICHARSET &unicharset);
|
||||
|
||||
/* dict.cpp ****************************************************************/
|
||||
|
||||
/// Initialize Dict class - load dawgs from [lang].traineddata and
|
||||
/// user-specified wordlist and parttern list.
|
||||
void Load();
|
||||
void End();
|
||||
|
||||
// Resets the document dictionary analogous to ResetAdaptiveClassifier.
|
||||
void ResetDocumentDictionary() {
|
||||
if (pending_words_ != NULL)
|
||||
pending_words_->clear();
|
||||
if (document_words_ != NULL)
|
||||
document_words_->clear();
|
||||
}
|
||||
|
||||
// Create unicharset adaptations of known, short lists of UTF-8 equivalent
|
||||
// characters (think all hyphen-like symbols). The first version of the
|
||||
// list is taken as equivalent for matching against the dictionary.
|
||||
void LoadEquivalenceList(const char *unichar_strings[]);
|
||||
|
||||
// Normalize all hyphen and apostrophes to the canonicalized one for
|
||||
// matching; pass everything else through as is. See LoadEquivalenceList().
|
||||
UNICHAR_ID NormalizeUnicharIdForMatch(UNICHAR_ID unichar_id) const;
|
||||
|
||||
/**
|
||||
* Returns the maximal permuter code (from ccstruct/ratngs.h) if in light
|
||||
* of the current state the letter at word_index in the given word
|
||||
* is allowed according to at least one of the dawgs in dawgs_,
|
||||
* otherwise returns NO_PERM.
|
||||
*
|
||||
* The state is described by void_dawg_args, which are interpreted as
|
||||
* DawgArgs and contain two relevant input vectors: active_dawgs and
|
||||
* constraints. Each entry in the active_dawgs vector contains an index
|
||||
* into the dawgs_ vector and an EDGE_REF that indicates the last edge
|
||||
* followed in the dawg. Each entry in the constraints vector contains
|
||||
* an index into the dawgs_ vector and an EDGE_REF that indicates an edge
|
||||
* in a pattern dawg followed to match a pattern. Currently constraints
|
||||
* are used to save the state of punctuation dawgs after leading
|
||||
* punctuation was found.
|
||||
*
|
||||
* Input:
|
||||
* At word_index 0 dawg_args->active_dawgs should contain an entry for each
|
||||
* dawg whose type has a bit set in kBeginningDawgsType,
|
||||
* dawg_args->constraints should be empty. EDGE_REFs in active_dawgs and
|
||||
* constraints vectors should be initialized to NO_EDGE. If hyphen state
|
||||
* needs to be applied, initial dawg_args->active_dawgs and
|
||||
* dawg_args->constrains can be copied from the saved hyphen state
|
||||
* (maintained by Dict).
|
||||
* For word_index > 0 the corresponding state (active_dawgs and constraints)
|
||||
* can be obtained from dawg_args->updated_* passed to def_letter_is_okay
|
||||
* for word_index-1.
|
||||
* Note: the function assumes that active_dags, constraints and updated_*
|
||||
* member variables of dawg_args are not NULL.
|
||||
*
|
||||
* Output:
|
||||
* The function fills in dawg_args->updated_active_dawgs vector with the
|
||||
* entries for dawgs that contain the word up to the letter at word_index.
|
||||
* The new constraints (if any) are added to dawg_args->updated_constraints,
|
||||
* the constraints from dawg_args->constraints are also copied into it.
|
||||
*
|
||||
* Detailed description:
|
||||
* In order to determine whether the word is still valid after considering
|
||||
* all the letters up to the one at word_index the following is done for
|
||||
* each entry in dawg_args->active_dawgs:
|
||||
*
|
||||
* - next starting node is obtained from entry.ref and edge_char_of() is
|
||||
* called to obtain the next edge
|
||||
* - if a valid edge is found, the function returns the updated permuter
|
||||
* code true and an entry [entry.dawg_index, edge] is inserted in
|
||||
* dawg_args->updated_active_dawgs
|
||||
* otherwise:
|
||||
* - if we are dealing with dawg of type DAWG_TYPE_PUNCTUATION,
|
||||
* edge_char_of() is called again, but now with kPatternUnicharID
|
||||
* as unichar_id; if a valid edge is found it is recorded in
|
||||
* dawg_args->updated_constraints
|
||||
* - the function checks whether the word can end with the previous
|
||||
* letter
|
||||
* - each successor of the dawg (e.g. dawgs with type DAWG_TYPE_WORD
|
||||
* could be successors to dawgs with type DAWG_TYPE_PUNCTUATION; the
|
||||
* successors are defined by successors_ vector) is explored and if
|
||||
* a letter is found in the successor dawg, a new entry is inserted
|
||||
* into dawg_args->updated_active_dawgs with EDGE_REF being either
|
||||
* NO_EDGE or an EDGE_REF recorded in constraints vector for the
|
||||
* corresponding dawg index
|
||||
*/
|
||||
|
||||
//
|
||||
int def_letter_is_okay(void* void_dawg_args,
|
||||
UNICHAR_ID unichar_id, bool word_end) const;
|
||||
|
||||
int (Dict::*letter_is_okay_)(void* void_dawg_args,
|
||||
UNICHAR_ID unichar_id, bool word_end) const;
|
||||
/// Calls letter_is_okay_ member function.
|
||||
int LetterIsOkay(void* void_dawg_args,
|
||||
UNICHAR_ID unichar_id, bool word_end) const {
|
||||
return (this->*letter_is_okay_)(void_dawg_args, unichar_id, word_end);
|
||||
}
|
||||
|
||||
|
||||
/// Probability in context function used by the ngram permuter.
|
||||
double (Dict::*probability_in_context_)(const char* lang,
|
||||
const char* context,
|
||||
int context_bytes,
|
||||
const char* character,
|
||||
int character_bytes);
|
||||
/// Calls probability_in_context_ member function.
|
||||
double ProbabilityInContext(const char* context,
|
||||
int context_bytes,
|
||||
const char* character,
|
||||
int character_bytes) {
|
||||
return (this->*probability_in_context_)(
|
||||
getImage()->getCCUtil()->lang.string(),
|
||||
context, context_bytes,
|
||||
character, character_bytes);
|
||||
}
|
||||
|
||||
/// Default (no-op) implementation of probability in context function.
|
||||
double def_probability_in_context(
|
||||
const char* lang, const char* context, int context_bytes,
|
||||
const char* character, int character_bytes) {
|
||||
(void) context;
|
||||
(void) context_bytes;
|
||||
(void) character;
|
||||
(void) character_bytes;
|
||||
return 0.0;
|
||||
}
|
||||
double ngram_probability_in_context(const char* lang,
|
||||
const char* context,
|
||||
int context_bytes,
|
||||
const char* character,
|
||||
int character_bytes);
|
||||
|
||||
/// Return the number of dawgs in the dawgs_ vector.
|
||||
inline const int NumDawgs() const { return dawgs_.size(); }
|
||||
/// Return i-th dawg pointer recorded in the dawgs_ vector.
|
||||
inline const Dawg *GetDawg(int index) const { return dawgs_[index]; }
|
||||
/// Return the points to the punctuation dawg.
|
||||
inline const Dawg *GetPuncDawg() const { return punc_dawg_; }
|
||||
/// Return the points to the unambiguous words dawg.
|
||||
inline const Dawg *GetUnambigDawg() const { return unambig_dawg_; }
|
||||
/// Return the pointer to the Dawg that contains words of length word_length.
|
||||
inline const Dawg *GetFixedLengthDawg(int word_length) const {
|
||||
if (word_length > max_fixed_length_dawgs_wdlen_) return NULL;
|
||||
assert(dawgs_.size() > word_length);
|
||||
return dawgs_[word_length];
|
||||
}
|
||||
inline const int GetMaxFixedLengthDawgIndex() const {
|
||||
return max_fixed_length_dawgs_wdlen_;
|
||||
}
|
||||
/// Returns the appropriate next node given the EDGE_REF.
|
||||
static inline NODE_REF GetStartingNode(const Dawg *dawg, EDGE_REF edge_ref) {
|
||||
if (edge_ref == NO_EDGE) return 0; // beginning to explore the dawg
|
||||
NODE_REF node = dawg->next_node(edge_ref);
|
||||
if (node == 0) node = NO_EDGE; // end of word
|
||||
return node;
|
||||
}
|
||||
/// At word ending make sure all the recorded constraints are satisfied.
|
||||
/// Each constraint signifies that we found a beginning pattern in a
|
||||
/// pattern dawg. Check that this pattern can end here (e.g. if some
|
||||
/// leading punctuation is found this would ensure that we are not
|
||||
/// expecting any particular trailing punctuation after the word).
|
||||
inline bool ConstraintsOk(const DawgInfoVector &constraints,
|
||||
int word_end, DawgType current_dawg_type) const {
|
||||
if (!word_end) return true;
|
||||
if (current_dawg_type == DAWG_TYPE_PUNCTUATION) return true;
|
||||
for (int c = 0; c < constraints.length(); ++c) {
|
||||
const DawgInfo &cinfo = constraints[c];
|
||||
Dawg *cdawg = dawgs_[cinfo.dawg_index];
|
||||
if (!cdawg->end_of_word(cinfo.ref)) {
|
||||
if (dawg_debug_level >= 3) {
|
||||
tprintf("Constraint [%d, " REFFORMAT "] is not satisfied\n",
|
||||
cinfo.dawg_index, cinfo.ref);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// For each of the character classes of the given unichar_id (and the
|
||||
/// unichar_id itself) finds the corresponding outgoing node or self-loop
|
||||
/// in the given dawg and (after checking that it is valid) records it in
|
||||
/// dawg_args->updated_ative_dawgs. Updates current_permuter if any valid
|
||||
/// edges were found.
|
||||
void ProcessPatternEdges(const Dawg *dawg, const DawgInfo &info,
|
||||
UNICHAR_ID unichar_id, bool word_end,
|
||||
DawgArgs *dawg_args,
|
||||
PermuterType *current_permuter) const;
|
||||
|
||||
/// Read/Write/Access special purpose dawgs which contain words
|
||||
/// only of a certain length (used for phrase search for
|
||||
/// non-space-delimited languages).
|
||||
|
||||
/// Reads a sequence of dawgs from the given file.
|
||||
/// Appends the constructed dawgs to the given dawg_vec.
|
||||
/// Fills the given table with indices of the dawgs in the
|
||||
/// dawg_vec corresponding to the dawgs with words
|
||||
/// of a particular length.
|
||||
static void ReadFixedLengthDawgs(DawgType type, const STRING &lang,
|
||||
PermuterType perm, int debug_level,
|
||||
FILE *file, DawgVector *dawg_vec,
|
||||
int *max_wdlen);
|
||||
/// Writes the dawgs in the dawgs_vec to a file. Updates the given table with
|
||||
/// the indices of dawgs in the dawg_vec for the corresponding word lengths.
|
||||
static void WriteFixedLengthDawgs(
|
||||
const GenericVector<SquishedDawg *> &dawg_vec,
|
||||
int num_dawgs, int debug_level, FILE *output_file);
|
||||
|
||||
/// Check all the DAWGs to see if this word is in any of them.
|
||||
inline static bool valid_word_permuter(uinT8 perm, bool numbers_ok) {
|
||||
return (perm == SYSTEM_DAWG_PERM || perm == FREQ_DAWG_PERM ||
|
||||
perm == DOC_DAWG_PERM || perm == USER_DAWG_PERM ||
|
||||
perm == USER_PATTERN_PERM || (numbers_ok && perm == NUMBER_PERM));
|
||||
}
|
||||
int valid_word(const WERD_CHOICE &word, bool numbers_ok) const;
|
||||
int valid_word(const WERD_CHOICE &word) const {
|
||||
return valid_word(word, false); // return NO_PERM for words with digits
|
||||
}
|
||||
int valid_word_or_number(const WERD_CHOICE &word) const {
|
||||
return valid_word(word, true); // return NUMBER_PERM for valid numbers
|
||||
}
|
||||
/// This function is used by api/tesseract_cube_combiner.cpp
|
||||
int valid_word(const char *string) const {
|
||||
WERD_CHOICE word(string, getUnicharset());
|
||||
return valid_word(word);
|
||||
}
|
||||
// Do the two WERD_CHOICEs form a meaningful bigram?
|
||||
bool valid_bigram(const WERD_CHOICE &word1, const WERD_CHOICE &word2) const;
|
||||
/// Returns true if the word contains a valid punctuation pattern.
|
||||
/// Note: Since the domains of punctuation symbols and symblos
|
||||
/// used in numbers are not disjoint, a valid number might contain
|
||||
/// an invalid punctuation pattern (e.g. .99).
|
||||
bool valid_punctuation(const WERD_CHOICE &word);
|
||||
/// Returns true if a good answer is found for the unknown blob rating.
|
||||
int good_choice(const WERD_CHOICE &choice);
|
||||
/// Adds a word found on this document to the document specific dictionary.
|
||||
void add_document_word(const WERD_CHOICE &best_choice);
|
||||
int get_top_word_script(const BLOB_CHOICE_LIST_VECTOR &char_choices,
|
||||
const UNICHARSET &unicharset);
|
||||
/// Adjusts the rating of the given word.
|
||||
void adjust_word(WERD_CHOICE *word, float *certainty_array,
|
||||
const BLOB_CHOICE_LIST_VECTOR *char_choices,
|
||||
bool nonword, float additional_adjust, bool debug);
|
||||
void adjust_word(WERD_CHOICE *word, float *certainty_array, bool debug) {
|
||||
adjust_word(word, certainty_array, NULL, false, 0.0f, debug);
|
||||
}
|
||||
void adjust_non_word(WERD_CHOICE *word, float *certainty_array, bool debug) {
|
||||
adjust_word(word, certainty_array, NULL, true, 0.0f, debug);
|
||||
}
|
||||
/// Set wordseg_rating_adjust_factor_ to the given value.
|
||||
inline void SetWordsegRatingAdjustFactor(float f) {
|
||||
wordseg_rating_adjust_factor_ = f;
|
||||
}
|
||||
// Accessor for best_choices_.
|
||||
const LIST &getBestChoices() { return best_choices_; }
|
||||
|
||||
private:
|
||||
/** Private member variables. */
|
||||
Image* image_ptr_;
|
||||
/**
|
||||
* Table that stores ambiguities computed during training
|
||||
* (loaded when NoDangerousAmbigs() is called for the first time).
|
||||
* Each entry i in the table stores a set of amibiguities whose
|
||||
* wrong ngram starts with unichar id i.
|
||||
*/
|
||||
UnicharAmbigs *dang_ambigs_table_;
|
||||
/** Same as above, but for ambiguities with replace flag set. */
|
||||
UnicharAmbigs *replace_ambigs_table_;
|
||||
/**
|
||||
* Flag used to disable accumulation of word choices
|
||||
* during compound word permutation.
|
||||
*/
|
||||
bool keep_word_choices_;
|
||||
/** Additional certainty padding allowed before a word is rejected. */
|
||||
FLOAT32 reject_offset_;
|
||||
/** Current word segmentation. */
|
||||
PIECES_STATE current_segmentation_;
|
||||
/** Variables to keep track of best/raw word choices. */
|
||||
VIABLE_CHOICE best_raw_choice_;
|
||||
LIST raw_choices_;
|
||||
LIST best_choices_;
|
||||
// Hyphen-related variables.
|
||||
UNICHAR_ID hyphen_unichar_id_;
|
||||
WERD_CHOICE *hyphen_word_;
|
||||
DawgInfoVector hyphen_active_dawgs_;
|
||||
DawgInfoVector hyphen_constraints_;
|
||||
bool last_word_on_line_;
|
||||
// List of lists of "equivalent" UNICHAR_IDs for the purposes of dictionary
|
||||
// matching. The first member of each list is taken as canonical. For
|
||||
// example, the first list contains hyphens and dashes with the first symbol
|
||||
// being the ASCII hyphen minus.
|
||||
GenericVector<GenericVectorEqEq<UNICHAR_ID> > equivalent_symbols_;
|
||||
// Dawgs.
|
||||
DawgVector dawgs_;
|
||||
SuccessorListsVector successors_;
|
||||
Trie *pending_words_;
|
||||
// bigram_dawg_ points to a dawg of two-word bigrams which always supercede if
|
||||
// any of them are present on the best choices list for a word pair.
|
||||
// the bigrams are stored as space-separated words where:
|
||||
// (1) leading and trailing punctuation has been removed from each word and
|
||||
// (2) any digits have been replaced with '?' marks.
|
||||
Dawg *bigram_dawg_;
|
||||
/// The following pointers are only cached for convenience.
|
||||
/// The dawgs will be deleted when dawgs_ vector is destroyed.
|
||||
// TODO(daria): need to support multiple languages in the future,
|
||||
// so maybe will need to maintain a list of dawgs of each kind.
|
||||
Dawg *freq_dawg_;
|
||||
Dawg *unambig_dawg_;
|
||||
Dawg *punc_dawg_;
|
||||
Trie *document_words_;
|
||||
/// Maximum word length of fixed-length word dawgs.
|
||||
/// A value < 1 indicates that no fixed-length dawgs are loaded.
|
||||
int max_fixed_length_dawgs_wdlen_;
|
||||
/// Current segmentation cost adjust factor for word rating.
|
||||
/// See comments in incorporate_segcost.
|
||||
float wordseg_rating_adjust_factor_;
|
||||
// File for recording ambiguities discovered during dictionary search.
|
||||
FILE *output_ambig_words_file_;
|
||||
|
||||
public:
|
||||
/// Variable members.
|
||||
/// These have to be declared and initialized after image_ptr_, which contains
|
||||
/// the pointer to the params vector - the member of its base CCUtil class.
|
||||
STRING_VAR_H(user_words_suffix, "", "A list of user-provided words.");
|
||||
STRING_VAR_H(user_patterns_suffix, "",
|
||||
"A list of user-provided patterns.");
|
||||
BOOL_VAR_H(load_system_dawg, true, "Load system word dawg.");
|
||||
BOOL_VAR_H(load_freq_dawg, true, "Load frequent word dawg.");
|
||||
BOOL_VAR_H(load_unambig_dawg, true, "Load unambiguous word dawg.");
|
||||
BOOL_VAR_H(load_punc_dawg, true,
|
||||
"Load dawg with punctuation patterns.");
|
||||
BOOL_VAR_H(load_number_dawg, true, "Load dawg with number patterns.");
|
||||
BOOL_VAR_H(load_fixed_length_dawgs, true, "Load fixed length"
|
||||
" dawgs (e.g. for non-space delimited languages)");
|
||||
BOOL_VAR_H(load_bigram_dawg, false,
|
||||
"Load dawg with special word bigrams.");
|
||||
double_VAR_H(segment_penalty_dict_frequent_word, 1.0,
|
||||
"Score multiplier for word matches which have good case and"
|
||||
"are frequent in the given language (lower is better).");
|
||||
|
||||
double_VAR_H(segment_penalty_dict_case_ok, 1.1,
|
||||
"Score multiplier for word matches that have good case "
|
||||
"(lower is better).");
|
||||
|
||||
double_VAR_H(segment_penalty_dict_case_bad, 1.3125,
|
||||
"Default score multiplier for word matches, which may have "
|
||||
"case issues (lower is better).");
|
||||
|
||||
// TODO(daria): remove this param when ngram permuter is deprecated.
|
||||
double_VAR_H(segment_penalty_ngram_best_choice, 1.24,
|
||||
"Multipler to for the best choice from the ngram model.");
|
||||
|
||||
double_VAR_H(segment_penalty_dict_nonword, 1.25,
|
||||
"Score multiplier for glyph fragment segmentations which "
|
||||
"do not match a dictionary word (lower is better).");
|
||||
|
||||
double_VAR_H(segment_penalty_garbage, 1.50,
|
||||
"Score multiplier for poorly cased strings that are not in"
|
||||
" the dictionary and generally look like garbage (lower is"
|
||||
" better).");
|
||||
STRING_VAR_H(output_ambig_words_file, "",
|
||||
"Output file for ambiguities found in the dictionary");
|
||||
INT_VAR_H(dawg_debug_level, 0, "Set to 1 for general debug info"
|
||||
", to 2 for more details, to 3 to see all the debug messages");
|
||||
INT_VAR_H(hyphen_debug_level, 0, "Debug level for hyphenated words.");
|
||||
INT_VAR_H(max_viterbi_list_size, 10, "Maximum size of viterbi list.");
|
||||
BOOL_VAR_H(use_only_first_uft8_step, false,
|
||||
"Use only the first UTF8 step of the given string"
|
||||
" when computing log probabilities.");
|
||||
double_VAR_H(certainty_scale, 20.0, "Certainty scaling factor");
|
||||
double_VAR_H(stopper_nondict_certainty_base, -2.50,
|
||||
"Certainty threshold for non-dict words");
|
||||
double_VAR_H(stopper_phase2_certainty_rejection_offset, 1.0,
|
||||
"Reject certainty offset");
|
||||
INT_VAR_H(stopper_smallword_size, 2,
|
||||
"Size of dict word to be treated as non-dict word");
|
||||
double_VAR_H(stopper_certainty_per_char, -0.50,
|
||||
"Certainty to add for each dict char above small word size.");
|
||||
double_VAR_H(stopper_allowable_character_badness, 3.0,
|
||||
"Max certaintly variation allowed in a word (in sigma)");
|
||||
INT_VAR_H(stopper_debug_level, 0, "Stopper debug level");
|
||||
BOOL_VAR_H(stopper_no_acceptable_choices, false,
|
||||
"Make AcceptableChoice() always return false. Useful"
|
||||
" when there is a need to explore all segmentations");
|
||||
double_VAR_H(stopper_ambiguity_threshold_gain, 8.0,
|
||||
"Gain factor for ambiguity threshold.");
|
||||
double_VAR_H(stopper_ambiguity_threshold_offset, 1.5,
|
||||
"Certainty offset for ambiguity threshold.");
|
||||
BOOL_VAR_H(save_raw_choices, false, "Save all explored raw choices");
|
||||
INT_VAR_H(tessedit_truncate_wordchoice_log, 10, "Max words to keep in list");
|
||||
STRING_VAR_H(word_to_debug, "", "Word for which stopper debug information"
|
||||
" should be printed to stdout");
|
||||
STRING_VAR_H(word_to_debug_lengths, "",
|
||||
"Lengths of unichars in word_to_debug");
|
||||
INT_VAR_H(fragments_debug, 0, "Debug character fragments");
|
||||
INT_VAR_H(segment_debug, 0, "Debug the whole segmentation process");
|
||||
BOOL_VAR_H(permute_debug, 0, "Debug char permutation process");
|
||||
double_VAR_H(bestrate_pruning_factor, 2.0, "Multiplying factor of"
|
||||
" current best rate to prune other hypotheses");
|
||||
BOOL_VAR_H(permute_script_word, 0,
|
||||
"Turn on word script consistency permuter");
|
||||
BOOL_VAR_H(segment_segcost_rating, 0,
|
||||
"incorporate segmentation cost in word rating?");
|
||||
BOOL_VAR_H(segment_nonalphabetic_script, false,
|
||||
"Don't use any alphabetic-specific tricks."
|
||||
"Set to true in the traineddata config file for"
|
||||
" scripts that are cursive or inherently fixed-pitch");
|
||||
double_VAR_H(segment_reward_script, 0.95,
|
||||
"Score multipler for script consistency within a word. "
|
||||
"Being a 'reward' factor, it should be <= 1. "
|
||||
"Smaller value implies bigger reward.");
|
||||
BOOL_VAR_H(permute_fixed_length_dawg, 0,
|
||||
"Turn on fixed-length phrasebook search permuter");
|
||||
BOOL_VAR_H(permute_chartype_word, 0,
|
||||
"Turn on character type (property) consistency permuter");
|
||||
double_VAR_H(segment_reward_chartype, 0.97,
|
||||
"Score multipler for char type consistency within a word. ");
|
||||
// TODO(daria): remove this param when ngram permuter is deprecated.
|
||||
double_VAR_H(segment_reward_ngram_best_choice, 0.99,
|
||||
"Score multipler for ngram permuter's best choice"
|
||||
" (only used in the Han script path).");
|
||||
BOOL_VAR_H(save_doc_words, 0, "Save Document Words");
|
||||
BOOL_VAR_H(doc_dict_enable, 1, "Enable Document Dictionary ");
|
||||
double_VAR_H(doc_dict_pending_threshold, 0.0,
|
||||
"Worst certainty for using pending dictionary");
|
||||
double_VAR_H(doc_dict_certainty_threshold, -2.25, "Worst certainty"
|
||||
" for words that can be inserted into the document dictionary");
|
||||
BOOL_VAR_H(ngram_permuter_activated, false,
|
||||
"Activate character-level n-gram-based permuter");
|
||||
INT_VAR_H(max_permuter_attempts, 10000, "Maximum number of different"
|
||||
" character choices to consider during permutation."
|
||||
" This limit is especially useful when user patterns"
|
||||
" are specified, since overly generic patterns can result in"
|
||||
" dawg search exploring an overly large number of options.");
|
||||
BOOL_VAR_H(permute_only_top, false, "Run only the top choice permuter");
|
||||
};
|
||||
} // namespace tesseract
|
||||
|
||||
#endif // THIRD_PARTY_TESSERACT_DICT_DICT_H_
|
|
@ -1,412 +0,0 @@
|
|||
/* -*-C-*-
|
||||
********************************************************************************
|
||||
*
|
||||
* File: trie.h (Formerly trie.h)
|
||||
* Description: Functions to build a trie data structure.
|
||||
* Author: Mark Seaman, SW Productivity
|
||||
* Created: Fri Oct 16 14:37:00 1987
|
||||
* Modified: Fri Jul 26 11:26:34 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 TRIE_H
|
||||
#define TRIE_H
|
||||
|
||||
#include "dawg.h"
|
||||
#include "cutil.h"
|
||||
#include "genericvector.h"
|
||||
|
||||
class UNICHARSET;
|
||||
|
||||
// Note: if we consider either NODE_REF or EDGE_INDEX to ever exceed
|
||||
// max int32, we will need to change GenericVector to use int64 for size
|
||||
// and address indices. This does not seem to be needed immediately,
|
||||
// since currently the largest number of edges limit used by tesseract
|
||||
// (kMaxNumEdges in wordlist2dawg.cpp) is far less than max int32.
|
||||
// There are also int casts below to satisfy the WIN32 compiler that would
|
||||
// need to be changed.
|
||||
// It might be cleanest to change the types of most of the Trie/Dawg related
|
||||
// typedefs to int and restrict the casts to extracting these values from
|
||||
// the 64 bit EDGE_RECORD.
|
||||
typedef inT64 EDGE_INDEX; // index of an edge in a given node
|
||||
typedef bool *NODE_MARKER;
|
||||
typedef GenericVector<EDGE_RECORD> EDGE_VECTOR;
|
||||
|
||||
struct TRIE_NODE_RECORD {
|
||||
EDGE_VECTOR forward_edges;
|
||||
EDGE_VECTOR backward_edges;
|
||||
};
|
||||
typedef GenericVector<TRIE_NODE_RECORD *> TRIE_NODES;
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
/**
|
||||
* Concrete class for Trie data structure that allows to store a list of
|
||||
* words (extends Dawg base class) as well as dynamically add new words.
|
||||
* This class stores a vector of pointers to TRIE_NODE_RECORDs, each of
|
||||
* which has a vector of forward and backward edges.
|
||||
*/
|
||||
class Trie : public Dawg {
|
||||
public:
|
||||
enum RTLReversePolicy {
|
||||
RRP_DO_NO_REVERSE,
|
||||
RRP_REVERSE_IF_HAS_RTL,
|
||||
RRP_FORCE_REVERSE,
|
||||
};
|
||||
|
||||
// Minimum number of concrete characters at the beginning of user patterns.
|
||||
static const int kSaneNumConcreteChars = 4;
|
||||
// Various unicode whitespace characters are used to denote unichar patterns,
|
||||
// (character classifier would never produce these whitespace characters as a
|
||||
// valid classification).
|
||||
static const char kAlphaPatternUnicode[];
|
||||
static const char kDigitPatternUnicode[];
|
||||
static const char kAlphanumPatternUnicode[];
|
||||
static const char kPuncPatternUnicode[];
|
||||
static const char kLowerPatternUnicode[];
|
||||
static const char kUpperPatternUnicode[];
|
||||
|
||||
static const char *get_reverse_policy_name(
|
||||
RTLReversePolicy reverse_policy);
|
||||
|
||||
// max_num_edges argument allows limiting the amount of memory this
|
||||
// Trie can consume (if a new word insert would cause the Trie to
|
||||
// contain more edges than max_num_edges, all the edges are cleared
|
||||
// so that new inserts can proceed).
|
||||
Trie(DawgType type, const STRING &lang, PermuterType perm,
|
||||
uinT64 max_num_edges, int unicharset_size, int debug_level) {
|
||||
init(type, lang, perm, unicharset_size, debug_level);
|
||||
num_edges_ = 0;
|
||||
max_num_edges_ = max_num_edges;
|
||||
deref_node_index_mask_ = ~letter_mask_;
|
||||
new_dawg_node(); // need to allocate node 0
|
||||
initialized_patterns_ = false;
|
||||
}
|
||||
virtual ~Trie() { nodes_.delete_data_pointers(); }
|
||||
|
||||
// Reset the Trie to empty.
|
||||
void clear();
|
||||
|
||||
/** Returns the edge that corresponds to the letter out of this node. */
|
||||
EDGE_REF edge_char_of(NODE_REF node_ref, UNICHAR_ID unichar_id,
|
||||
bool word_end) const {
|
||||
EDGE_RECORD *edge_ptr;
|
||||
EDGE_INDEX edge_index;
|
||||
if (!edge_char_of(node_ref, NO_EDGE, FORWARD_EDGE, word_end, unichar_id,
|
||||
&edge_ptr, &edge_index)) return NO_EDGE;
|
||||
return make_edge_ref(node_ref, edge_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the given NodeChildVector with all the unichar ids (and the
|
||||
* corresponding EDGE_REFs) for which there is an edge out of this node.
|
||||
*/
|
||||
void unichar_ids_of(NODE_REF node, NodeChildVector *vec) const {
|
||||
const EDGE_VECTOR &forward_edges =
|
||||
nodes_[static_cast<int>(node)]->forward_edges;
|
||||
for (int i = 0; i < forward_edges.size(); ++i) {
|
||||
vec->push_back(NodeChild(unichar_id_from_edge_rec(forward_edges[i]),
|
||||
make_edge_ref(node, i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next node visited by following the edge
|
||||
* indicated by the given EDGE_REF.
|
||||
*/
|
||||
NODE_REF next_node(EDGE_REF edge_ref) const {
|
||||
if (edge_ref == NO_EDGE || num_edges_ == 0) return NO_EDGE;
|
||||
return next_node_from_edge_rec(*deref_edge_ref(edge_ref));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the edge indicated by the given EDGE_REF
|
||||
* marks the end of a word.
|
||||
*/
|
||||
bool end_of_word(EDGE_REF edge_ref) const {
|
||||
if (edge_ref == NO_EDGE || num_edges_ == 0) return false;
|
||||
return end_of_word_from_edge_rec(*deref_edge_ref(edge_ref));
|
||||
}
|
||||
|
||||
/** Returns UNICHAR_ID stored in the edge indicated by the given EDGE_REF. */
|
||||
UNICHAR_ID edge_letter(EDGE_REF edge_ref) const {
|
||||
if (edge_ref == NO_EDGE || num_edges_ == 0) return INVALID_UNICHAR_ID;
|
||||
return unichar_id_from_edge_rec(*deref_edge_ref(edge_ref));
|
||||
}
|
||||
|
||||
// Prints the contents of the node indicated by the given NODE_REF.
|
||||
// At most max_num_edges will be printed.
|
||||
void print_node(NODE_REF node, int max_num_edges) const;
|
||||
|
||||
// Writes edges from nodes_ to an EDGE_ARRAY and creates a SquishedDawg.
|
||||
// Eliminates redundant edges and returns the pointer to the SquishedDawg.
|
||||
// Note: the caller is responsible for deallocating memory associated
|
||||
// with the returned SquishedDawg pointer.
|
||||
SquishedDawg *trie_to_dawg();
|
||||
|
||||
// Inserts the list of words from the given file into the Trie.
|
||||
// If reverse is true, calls WERD_CHOICE::reverse_unichar_ids_if_rtl()
|
||||
// on each word before inserting it into the Trie.
|
||||
bool read_word_list(const char *filename,
|
||||
const UNICHARSET &unicharset,
|
||||
Trie::RTLReversePolicy reverse);
|
||||
|
||||
// Inserts the list of patterns from the given file into the Trie.
|
||||
// The pattern list file should contain one pattern per line in UTF-8 format.
|
||||
//
|
||||
// Each pattern can contain any non-whitespace characters, however only the
|
||||
// patterns that contain characters from the unicharset of the corresponding
|
||||
// language will be useful.
|
||||
// The only meta character is '\'. To be used in a pattern as an ordinary
|
||||
// string it should be escaped with '\' (e.g. string "C:\Documents" should
|
||||
// be written in the patterns file as "C:\\Documents").
|
||||
// This function supports a very limited regular expression syntax. One can
|
||||
// express a character, a certain character class and a number of times the
|
||||
// entity should be repeated in the pattern.
|
||||
//
|
||||
// To denote a character class use one of:
|
||||
// \c - unichar for which UNICHARSET::get_isalpha() is true (character)
|
||||
// \d - unichar for which UNICHARSET::get_isdigit() is true
|
||||
// \n - unichar for which UNICHARSET::get_isdigit() and
|
||||
// UNICHARSET::isalpha() are true
|
||||
// \p - unichar for which UNICHARSET::get_ispunct() is true
|
||||
// \a - unichar for which UNICHARSET::get_islower() is true
|
||||
// \A - unichar for which UNICHARSET::get_isupper() is true
|
||||
//
|
||||
// \* could be specified after each character or pattern to indicate that
|
||||
// the character/pattern can be repeated any number of times before the next
|
||||
// character/pattern occurs.
|
||||
//
|
||||
// Examples:
|
||||
// 1-8\d\d-GOOG-411 will be expanded to strings:
|
||||
// 1-800-GOOG-411, 1-801-GOOG-411, ... 1-899-GOOG-411.
|
||||
//
|
||||
// http://www.\n\*.com will be expanded to strings like:
|
||||
// http://www.a.com http://www.a123.com ... http://www.ABCDefgHIJKLMNop.com
|
||||
//
|
||||
// Note: In choosing which patterns to include please be aware of the fact
|
||||
// providing very generic patterns will make tesseract run slower.
|
||||
// For example \n\* at the beginning of the pattern will make Tesseract
|
||||
// consider all the combinations of proposed character choices for each
|
||||
// of the segmentations, which will be unacceptably slow.
|
||||
// Because of potential problems with speed that could be difficult to
|
||||
// identify, each user pattern has to have at least kSaneNumConcreteChars
|
||||
// concrete characters from the unicharset at the beginning.
|
||||
bool read_pattern_list(const char *filename, const UNICHARSET &unicharset);
|
||||
|
||||
// Initializes the values of *_pattern_ unichar ids.
|
||||
// This function should be called before calling read_pattern_list().
|
||||
void initialize_patterns(UNICHARSET *unicharset);
|
||||
|
||||
// Fills in the given unichar id vector with the unichar ids that represent
|
||||
// the patterns of the character classes of the given unichar_id.
|
||||
void unichar_id_to_patterns(UNICHAR_ID unichar_id,
|
||||
const UNICHARSET &unicharset,
|
||||
GenericVector<UNICHAR_ID> *vec) const;
|
||||
|
||||
// Returns the given EDGE_REF if the EDGE_RECORD that it points to has
|
||||
// a self loop and the given unichar_id matches the unichar_id stored in the
|
||||
// EDGE_RECORD, returns NO_EDGE otherwise.
|
||||
virtual EDGE_REF pattern_loop_edge(EDGE_REF edge_ref,
|
||||
UNICHAR_ID unichar_id,
|
||||
bool word_end) const {
|
||||
if (edge_ref == NO_EDGE) return NO_EDGE;
|
||||
EDGE_RECORD *edge_rec = deref_edge_ref(edge_ref);
|
||||
return (marker_flag_from_edge_rec(*edge_rec) &&
|
||||
unichar_id == unichar_id_from_edge_rec(*edge_rec) &&
|
||||
word_end == end_of_word_from_edge_rec(*edge_rec)) ?
|
||||
edge_ref : NO_EDGE;
|
||||
}
|
||||
|
||||
// Adds a word to the Trie (creates the necessary nodes and edges).
|
||||
//
|
||||
// If repetitions vector is not NULL, each entry in the vector indicates
|
||||
// whether the unichar id with the corresponding index in the word is allowed
|
||||
// to repeat an unlimited number of times. For each entry that is true, MARKER
|
||||
// flag of the corresponding edge created for this unichar id is set to true).
|
||||
//
|
||||
// Return true if add succeeded, false otherwise (e.g. when a word contained
|
||||
// an invalid unichar id or the trie was getting too large and was cleared).
|
||||
bool add_word_to_dawg(const WERD_CHOICE &word,
|
||||
const GenericVector<bool> *repetitions);
|
||||
bool add_word_to_dawg(const WERD_CHOICE &word) {
|
||||
return add_word_to_dawg(word, NULL);
|
||||
}
|
||||
|
||||
protected:
|
||||
// The structure of an EDGE_REF for Trie edges is as follows:
|
||||
// [LETTER_START_BIT, flag_start_bit_):
|
||||
// edge index in *_edges in a TRIE_NODE_RECORD
|
||||
// [flag_start_bit, 30th bit]: node index in nodes (TRIE_NODES vector)
|
||||
//
|
||||
// With this arrangement there are enough bits to represent edge indices
|
||||
// (each node can have at most unicharset_size_ forward edges and
|
||||
// the position of flag_start_bit is set to be log2(unicharset_size_)).
|
||||
// It is also possible to accommodate a maximum number of nodes that is at
|
||||
// least as large as that of the SquishedDawg representation (in SquishedDawg
|
||||
// each EDGE_RECORD has 32-(flag_start_bit+NUM_FLAG_BITS) bits to represent
|
||||
// the next node index).
|
||||
//
|
||||
|
||||
// Returns the pointer to EDGE_RECORD after decoding the location
|
||||
// of the edge from the information in the given EDGE_REF.
|
||||
// This function assumes that EDGE_REF holds valid node/edge indices.
|
||||
inline EDGE_RECORD *deref_edge_ref(EDGE_REF edge_ref) const {
|
||||
int edge_index = static_cast<int>(
|
||||
(edge_ref & letter_mask_) >> LETTER_START_BIT);
|
||||
int node_index = static_cast<int>(
|
||||
(edge_ref & deref_node_index_mask_) >> flag_start_bit_);
|
||||
TRIE_NODE_RECORD *node_rec = nodes_[node_index];
|
||||
return &(node_rec->forward_edges[edge_index]);
|
||||
}
|
||||
/** Constructs EDGE_REF from the given node_index and edge_index. */
|
||||
inline EDGE_REF make_edge_ref(NODE_REF node_index,
|
||||
EDGE_INDEX edge_index) const {
|
||||
return ((node_index << flag_start_bit_) |
|
||||
(edge_index << LETTER_START_BIT));
|
||||
}
|
||||
/** Sets up this edge record to the requested values. */
|
||||
inline void link_edge(EDGE_RECORD *edge, NODE_REF nxt, bool repeats,
|
||||
int direction, bool word_end, UNICHAR_ID unichar_id) {
|
||||
EDGE_RECORD flags = 0;
|
||||
if (repeats) flags |= MARKER_FLAG;
|
||||
if (word_end) flags |= WERD_END_FLAG;
|
||||
if (direction == BACKWARD_EDGE) flags |= DIRECTION_FLAG;
|
||||
*edge = ((nxt << next_node_start_bit_) |
|
||||
(static_cast<EDGE_RECORD>(flags) << flag_start_bit_) |
|
||||
(static_cast<EDGE_RECORD>(unichar_id) << LETTER_START_BIT));
|
||||
}
|
||||
/** Prints the given EDGE_RECORD. */
|
||||
inline void print_edge_rec(const EDGE_RECORD &edge_rec) const {
|
||||
tprintf("|" REFFORMAT "|%s%s%s|%d|", next_node_from_edge_rec(edge_rec),
|
||||
marker_flag_from_edge_rec(edge_rec) ? "R," : "",
|
||||
(direction_from_edge_rec(edge_rec) == FORWARD_EDGE) ? "F" : "B",
|
||||
end_of_word_from_edge_rec(edge_rec) ? ",E" : "",
|
||||
unichar_id_from_edge_rec(edge_rec));
|
||||
}
|
||||
// Returns true if the next node in recorded the given EDGE_RECORD
|
||||
// has exactly one forward edge.
|
||||
inline bool can_be_eliminated(const EDGE_RECORD &edge_rec) {
|
||||
NODE_REF node_ref = next_node_from_edge_rec(edge_rec);
|
||||
return (node_ref != NO_EDGE &&
|
||||
nodes_[static_cast<int>(node_ref)]->forward_edges.size() == 1);
|
||||
}
|
||||
|
||||
// Prints the contents of the Trie.
|
||||
// At most max_num_edges will be printed for each node.
|
||||
void print_all(const char* msg, int max_num_edges) {
|
||||
tprintf("\n__________________________\n%s\n", msg);
|
||||
for (int i = 0; i < nodes_.size(); ++i) print_node(i, max_num_edges);
|
||||
tprintf("__________________________\n");
|
||||
}
|
||||
|
||||
// Finds the edge with the given direction, word_end and unichar_id
|
||||
// in the node indicated by node_ref. Fills in the pointer to the
|
||||
// EDGE_RECORD and the index of the edge with the the values
|
||||
// corresponding to the edge found. Returns true if an edge was found.
|
||||
bool edge_char_of(NODE_REF node_ref, NODE_REF next_node,
|
||||
int direction, bool word_end, UNICHAR_ID unichar_id,
|
||||
EDGE_RECORD **edge_ptr, EDGE_INDEX *edge_index) const;
|
||||
|
||||
// Adds an single edge linkage between node1 and node2 in the direction
|
||||
// indicated by direction argument.
|
||||
bool add_edge_linkage(NODE_REF node1, NODE_REF node2, bool repeats,
|
||||
int direction, bool word_end,
|
||||
UNICHAR_ID unichar_id);
|
||||
|
||||
// Adds forward edge linkage from node1 to node2 and the corresponding
|
||||
// backward edge linkage in the other direction.
|
||||
bool add_new_edge(NODE_REF node1, NODE_REF node2,
|
||||
bool repeats, bool word_end, UNICHAR_ID unichar_id) {
|
||||
return (add_edge_linkage(node1, node2, repeats, FORWARD_EDGE,
|
||||
word_end, unichar_id) &&
|
||||
add_edge_linkage(node2, node1, repeats, BACKWARD_EDGE,
|
||||
word_end, unichar_id));
|
||||
}
|
||||
|
||||
// Sets the word ending flags in an already existing edge pair.
|
||||
// Returns true on success.
|
||||
void add_word_ending(EDGE_RECORD *edge,
|
||||
NODE_REF the_next_node,
|
||||
bool repeats,
|
||||
UNICHAR_ID unichar_id);
|
||||
|
||||
// Allocates space for a new node in the Trie.
|
||||
NODE_REF new_dawg_node();
|
||||
|
||||
// Removes a single edge linkage to between node1 and node2 in the
|
||||
// direction indicated by direction argument.
|
||||
void remove_edge_linkage(NODE_REF node1, NODE_REF node2, int direction,
|
||||
bool word_end, UNICHAR_ID unichar_id);
|
||||
|
||||
// Removes forward edge linkage from node1 to node2 and the corresponding
|
||||
// backward edge linkage in the other direction.
|
||||
void remove_edge(NODE_REF node1, NODE_REF node2,
|
||||
bool word_end, UNICHAR_ID unichar_id) {
|
||||
remove_edge_linkage(node1, node2, FORWARD_EDGE, word_end, unichar_id);
|
||||
remove_edge_linkage(node2, node1, BACKWARD_EDGE, word_end, unichar_id);
|
||||
}
|
||||
|
||||
// Compares edge1 and edge2 in the given node to see if they point to two
|
||||
// next nodes that could be collapsed. If they do, performs the reduction
|
||||
// and returns true.
|
||||
bool eliminate_redundant_edges(NODE_REF node, const EDGE_RECORD &edge1,
|
||||
const EDGE_RECORD &edge2);
|
||||
|
||||
// Assuming that edge_index indicates the first edge in a group of edges
|
||||
// in this node with a particular letter value, looks through these edges
|
||||
// to see if any of them can be collapsed. If so does it. Returns to the
|
||||
// caller when all edges with this letter have been reduced.
|
||||
// Returns true if further reduction is possible with this same letter.
|
||||
bool reduce_lettered_edges(EDGE_INDEX edge_index,
|
||||
UNICHAR_ID unichar_id,
|
||||
NODE_REF node,
|
||||
const EDGE_VECTOR &backward_edges,
|
||||
NODE_MARKER reduced_nodes);
|
||||
|
||||
/**
|
||||
* Order num_edges of consequtive EDGE_RECORDS in the given EDGE_VECTOR in
|
||||
* increasing order of unichar ids. This function is normally called
|
||||
* for all edges in a single node, and since number of edges in each node
|
||||
* is usually quite small, selection sort is used.
|
||||
*/
|
||||
void sort_edges(EDGE_VECTOR *edges);
|
||||
|
||||
/** Eliminates any redundant edges from this node in the Trie. */
|
||||
void reduce_node_input(NODE_REF node, NODE_MARKER reduced_nodes);
|
||||
|
||||
// Returns the pattern unichar id for the given character class code.
|
||||
UNICHAR_ID character_class_to_pattern(char ch);
|
||||
|
||||
// Member variables
|
||||
TRIE_NODES nodes_; // vector of nodes in the Trie
|
||||
uinT64 num_edges_; // sum of all edges (forward and backward)
|
||||
uinT64 max_num_edges_; // maximum number of edges allowed
|
||||
uinT64 deref_direction_mask_; // mask for EDGE_REF to extract direction
|
||||
uinT64 deref_node_index_mask_; // mask for EDGE_REF to extract node index
|
||||
// Variables for translating character class codes denoted in user patterns
|
||||
// file to the unichar ids used to represent them in a Trie.
|
||||
bool initialized_patterns_;
|
||||
UNICHAR_ID alpha_pattern_;
|
||||
UNICHAR_ID digit_pattern_;
|
||||
UNICHAR_ID alphanum_pattern_;
|
||||
UNICHAR_ID punc_pattern_;
|
||||
UNICHAR_ID lower_pattern_;
|
||||
UNICHAR_ID upper_pattern_;
|
||||
};
|
||||
} // namespace tesseract
|
||||
|
||||
#endif
|
|
@ -1,101 +0,0 @@
|
|||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "windows.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,2,0,0
|
||||
PRODUCTVERSION 3,2,0,0
|
||||
FILEFLAGSMASK 0x17L
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "FileDescription", "Generate sets of words Tesseract is likely to find ambiguous"
|
||||
VALUE "FileVersion", "3,2,0,0"
|
||||
VALUE "InternalName", "ambiguous_words"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2012 Google, Inc. Licensed under the Apache License, Version 2.0"
|
||||
VALUE "OriginalFilename", "ambiguous_words.exe"
|
||||
VALUE "ProductName", "Tesseract-OCR"
|
||||
VALUE "ProductVersion", "3.02"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
|
@ -1,674 +0,0 @@
|
|||
/* -*-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 "ccstruct.h"
|
||||
#include "clst.h"
|
||||
#include "cutil.h"
|
||||
#include "emalloc.h"
|
||||
#include "helpers.h"
|
||||
#include "ndminx.h"
|
||||
#include "normalis.h"
|
||||
#include "ocrblock.h"
|
||||
#include "ocrrow.h"
|
||||
#include "points.h"
|
||||
#include "polyaprx.h"
|
||||
#include "structures.h"
|
||||
#include "werd.h"
|
||||
|
||||
using tesseract::CCStruct;
|
||||
|
||||
// A Vector representing the "vertical" direction when measuring the
|
||||
// divisiblity of blobs into multiple blobs just by separating outlines.
|
||||
// See divisible_blob below for the use.
|
||||
const TPOINT kDivisibleVerticalUpright(0, 1);
|
||||
// A vector representing the "vertical" direction for italic text for use
|
||||
// when separating outlines. Using it actually deteriorates final accuracy,
|
||||
// so it is only used for ApplyBoxes chopping to get a better segmentation.
|
||||
const TPOINT kDivisibleVerticalItalic(1, 5);
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
F u n c t i o n s
|
||||
----------------------------------------------------------------------*/
|
||||
|
||||
CLISTIZE(EDGEPT);
|
||||
|
||||
// Consume the circular list of EDGEPTs to make a TESSLINE.
|
||||
TESSLINE* TESSLINE::BuildFromOutlineList(EDGEPT* outline) {
|
||||
TESSLINE* result = new TESSLINE;
|
||||
result->loop = outline;
|
||||
result->SetupFromPos();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Copies the data and the outline, but leaves next untouched.
|
||||
void TESSLINE::CopyFrom(const TESSLINE& src) {
|
||||
Clear();
|
||||
topleft = src.topleft;
|
||||
botright = src.botright;
|
||||
start = src.start;
|
||||
is_hole = src.is_hole;
|
||||
if (src.loop != NULL) {
|
||||
EDGEPT* prevpt = NULL;
|
||||
EDGEPT* newpt = NULL;
|
||||
EDGEPT* srcpt = src.loop;
|
||||
do {
|
||||
newpt = new EDGEPT(*srcpt);
|
||||
if (prevpt == NULL) {
|
||||
loop = newpt;
|
||||
} else {
|
||||
newpt->prev = prevpt;
|
||||
prevpt->next = newpt;
|
||||
}
|
||||
prevpt = newpt;
|
||||
srcpt = srcpt->next;
|
||||
} while (srcpt != src.loop);
|
||||
loop->prev = newpt;
|
||||
newpt->next = loop;
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes owned data.
|
||||
void TESSLINE::Clear() {
|
||||
if (loop == NULL)
|
||||
return;
|
||||
|
||||
EDGEPT* this_edge = loop;
|
||||
do {
|
||||
EDGEPT* next_edge = this_edge->next;
|
||||
delete this_edge;
|
||||
this_edge = next_edge;
|
||||
} while (this_edge != loop);
|
||||
loop = NULL;
|
||||
}
|
||||
|
||||
// Normalize in-place using the DENORM.
|
||||
void TESSLINE::Normalize(const DENORM& denorm) {
|
||||
EDGEPT* pt = loop;
|
||||
do {
|
||||
denorm.LocalNormTransform(pt->pos, &pt->pos);
|
||||
pt = pt->next;
|
||||
} while (pt != loop);
|
||||
SetupFromPos();
|
||||
}
|
||||
|
||||
// Rotates by the given rotation in place.
|
||||
void TESSLINE::Rotate(const FCOORD rot) {
|
||||
EDGEPT* pt = loop;
|
||||
do {
|
||||
int tmp = static_cast<int>(floor(pt->pos.x * rot.x() -
|
||||
pt->pos.y * rot.y() + 0.5));
|
||||
pt->pos.y = static_cast<int>(floor(pt->pos.y * rot.x() +
|
||||
pt->pos.x * rot.y() + 0.5));
|
||||
pt->pos.x = tmp;
|
||||
pt = pt->next;
|
||||
} while (pt != loop);
|
||||
SetupFromPos();
|
||||
}
|
||||
|
||||
// Moves by the given vec in place.
|
||||
void TESSLINE::Move(const ICOORD vec) {
|
||||
EDGEPT* pt = loop;
|
||||
do {
|
||||
pt->pos.x += vec.x();
|
||||
pt->pos.y += vec.y();
|
||||
pt = pt->next;
|
||||
} while (pt != loop);
|
||||
SetupFromPos();
|
||||
}
|
||||
|
||||
// Scales by the given factor in place.
|
||||
void TESSLINE::Scale(float factor) {
|
||||
EDGEPT* pt = loop;
|
||||
do {
|
||||
pt->pos.x = static_cast<int>(floor(pt->pos.x * factor + 0.5));
|
||||
pt->pos.y = static_cast<int>(floor(pt->pos.y * factor + 0.5));
|
||||
pt = pt->next;
|
||||
} while (pt != loop);
|
||||
SetupFromPos();
|
||||
}
|
||||
|
||||
// Sets up the start and vec members of the loop from the pos members.
|
||||
void TESSLINE::SetupFromPos() {
|
||||
EDGEPT* pt = loop;
|
||||
do {
|
||||
pt->vec.x = pt->next->pos.x - pt->pos.x;
|
||||
pt->vec.y = pt->next->pos.y - pt->pos.y;
|
||||
pt = pt->next;
|
||||
} while (pt != loop);
|
||||
start = pt->pos;
|
||||
ComputeBoundingBox();
|
||||
}
|
||||
|
||||
// Recomputes the bounding box from the points in the loop.
|
||||
void TESSLINE::ComputeBoundingBox() {
|
||||
int minx = MAX_INT32;
|
||||
int miny = MAX_INT32;
|
||||
int maxx = -MAX_INT32;
|
||||
int maxy = -MAX_INT32;
|
||||
|
||||
// Find boundaries.
|
||||
start = loop->pos;
|
||||
EDGEPT* this_edge = loop;
|
||||
do {
|
||||
if (!this_edge->IsHidden() || !this_edge->prev->IsHidden()) {
|
||||
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 != loop);
|
||||
// Reset bounds.
|
||||
topleft.x = minx;
|
||||
topleft.y = maxy;
|
||||
botright.x = maxx;
|
||||
botright.y = miny;
|
||||
}
|
||||
|
||||
// Computes the min and max cross product of the outline points with the
|
||||
// given vec and returns the results in min_xp and max_xp. Geometrically
|
||||
// this is the left and right edge of the outline perpendicular to the
|
||||
// given direction, but to get the distance units correct, you would
|
||||
// have to divide by the modulus of vec.
|
||||
void TESSLINE::MinMaxCrossProduct(const TPOINT vec,
|
||||
int* min_xp, int* max_xp) const {
|
||||
*min_xp = MAX_INT32;
|
||||
*max_xp = MIN_INT32;
|
||||
EDGEPT* this_edge = loop;
|
||||
do {
|
||||
if (!this_edge->IsHidden() || !this_edge->prev->IsHidden()) {
|
||||
int product = CROSS(this_edge->pos, vec);
|
||||
UpdateRange(product, min_xp, max_xp);
|
||||
}
|
||||
this_edge = this_edge->next;
|
||||
} while (this_edge != loop);
|
||||
}
|
||||
|
||||
TBOX TESSLINE::bounding_box() const {
|
||||
return TBOX(topleft.x, botright.y, botright.x, topleft.y);
|
||||
}
|
||||
|
||||
void TESSLINE::plot(ScrollView* window, ScrollView::Color color,
|
||||
ScrollView::Color child_color) {
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (is_hole)
|
||||
window->Pen(child_color);
|
||||
else
|
||||
window->Pen(color);
|
||||
window->SetCursor(start.x, start.y);
|
||||
EDGEPT* pt = loop;
|
||||
do {
|
||||
bool prev_hidden = pt->IsHidden();
|
||||
pt = pt->next;
|
||||
if (prev_hidden)
|
||||
window->SetCursor(pt->pos.x, pt->pos.y);
|
||||
else
|
||||
window->DrawTo(pt->pos.x, pt->pos.y);
|
||||
} while (pt != loop);
|
||||
#endif // GRAPHICS_DISABLED
|
||||
}
|
||||
|
||||
// Iterate the given list of outlines, converting to TESSLINE by polygonal
|
||||
// approximation and recursively any children, returning the current tail
|
||||
// of the resulting list of TESSLINEs.
|
||||
static TESSLINE** ApproximateOutlineList(C_OUTLINE_LIST* outlines,
|
||||
bool children,
|
||||
TESSLINE** tail) {
|
||||
C_OUTLINE_IT ol_it(outlines);
|
||||
for (ol_it.mark_cycle_pt(); !ol_it.cycled_list(); ol_it.forward()) {
|
||||
C_OUTLINE* outline = ol_it.data();
|
||||
TESSLINE* tessline = ApproximateOutline(outline);
|
||||
tessline->is_hole = children;
|
||||
*tail = tessline;
|
||||
tail = &tessline->next;
|
||||
if (!outline->child()->empty()) {
|
||||
tail = ApproximateOutlineList(outline->child(), true, tail);
|
||||
}
|
||||
}
|
||||
return tail;
|
||||
}
|
||||
|
||||
// Factory to build a TBLOB from a C_BLOB with polygonal
|
||||
// approximation along the way.
|
||||
TBLOB* TBLOB::PolygonalCopy(C_BLOB* src) {
|
||||
C_OUTLINE_IT ol_it = src->out_list();
|
||||
TBLOB* tblob = new TBLOB;
|
||||
ApproximateOutlineList(src->out_list(), false, &tblob->outlines);
|
||||
return tblob;
|
||||
}
|
||||
|
||||
// Normalizes the blob for classification only if needed.
|
||||
// (Normally this means a non-zero classify rotation.)
|
||||
// If no Normalization is needed, then NULL is returned, and the denorm is
|
||||
// unchanged. Otherwise a new TBLOB is returned and the denorm points to
|
||||
// a new DENORM. In this case, both the TBLOB and DENORM must be deleted.
|
||||
TBLOB* TBLOB::ClassifyNormalizeIfNeeded(const DENORM** denorm) const {
|
||||
TBLOB* rotated_blob = NULL;
|
||||
// If necessary, copy the blob and rotate it. The rotation is always
|
||||
// +/- 90 degrees, as 180 was already taken care of.
|
||||
if ((*denorm)->block() != NULL &&
|
||||
(*denorm)->block()->classify_rotation().y() != 0.0) {
|
||||
TBOX box = bounding_box();
|
||||
int x_middle = (box.left() + box.right()) / 2;
|
||||
int y_middle = (box.top() + box.bottom()) / 2;
|
||||
rotated_blob = new TBLOB(*this);
|
||||
const FCOORD& rotation = (*denorm)->block()->classify_rotation();
|
||||
DENORM* norm = new DENORM;
|
||||
// Move the rotated blob back to the same y-position so that we
|
||||
// can still distinguish similar glyphs with differeny y-position.
|
||||
float target_y = kBlnBaselineOffset +
|
||||
(rotation.y() > 0 ? x_middle - box.left() : box.right() - x_middle);
|
||||
norm->SetupNormalization(NULL, NULL, &rotation, *denorm, NULL, 0,
|
||||
x_middle, y_middle, 1.0f, 1.0f, 0.0f, target_y);
|
||||
// x_middle, y_middle, 1.0f, 1.0f, 0.0f, y_middle);
|
||||
rotated_blob->Normalize(*norm);
|
||||
*denorm = norm;
|
||||
}
|
||||
return rotated_blob;
|
||||
}
|
||||
|
||||
// Copies the data and the outline, but leaves next untouched.
|
||||
void TBLOB::CopyFrom(const TBLOB& src) {
|
||||
Clear();
|
||||
TESSLINE* prev_outline = NULL;
|
||||
for (TESSLINE* srcline = src.outlines; srcline != NULL;
|
||||
srcline = srcline->next) {
|
||||
TESSLINE* new_outline = new TESSLINE(*srcline);
|
||||
if (outlines == NULL)
|
||||
outlines = new_outline;
|
||||
else
|
||||
prev_outline->next = new_outline;
|
||||
prev_outline = new_outline;
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes owned data.
|
||||
void TBLOB::Clear() {
|
||||
for (TESSLINE* next_outline = NULL; outlines != NULL;
|
||||
outlines = next_outline) {
|
||||
next_outline = outlines->next;
|
||||
delete outlines;
|
||||
}
|
||||
}
|
||||
// Normalize in-place using the DENORM.
|
||||
void TBLOB::Normalize(const DENORM& denorm) {
|
||||
// TODO(rays) outline->Normalize is more accurate, but breaks tests due
|
||||
// the changes it makes. Reinstate this code with a retraining.
|
||||
#if 1
|
||||
for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
|
||||
outline->Normalize(denorm);
|
||||
}
|
||||
#else
|
||||
denorm.LocalNormBlob(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Rotates by the given rotation in place.
|
||||
void TBLOB::Rotate(const FCOORD rotation) {
|
||||
for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
|
||||
outline->Rotate(rotation);
|
||||
}
|
||||
}
|
||||
|
||||
// Moves by the given vec in place.
|
||||
void TBLOB::Move(const ICOORD vec) {
|
||||
for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
|
||||
outline->Move(vec);
|
||||
}
|
||||
}
|
||||
|
||||
// Scales by the given factor in place.
|
||||
void TBLOB::Scale(float factor) {
|
||||
for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
|
||||
outline->Scale(factor);
|
||||
}
|
||||
}
|
||||
|
||||
// Recomputes the bounding boxes of the outlines.
|
||||
void TBLOB::ComputeBoundingBoxes() {
|
||||
for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next) {
|
||||
outline->ComputeBoundingBox();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the number of outlines.
|
||||
int TBLOB::NumOutlines() const {
|
||||
int result = 0;
|
||||
for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next)
|
||||
++result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* TBLOB::bounding_box()
|
||||
*
|
||||
* Compute the bounding_box of a compound blob, defined to be the
|
||||
* bounding box of the union of all top-level outlines in the blob.
|
||||
**********************************************************************/
|
||||
TBOX TBLOB::bounding_box() const {
|
||||
if (outlines == NULL)
|
||||
return TBOX(0, 0, 0, 0);
|
||||
TESSLINE *outline = outlines;
|
||||
TBOX box = outline->bounding_box();
|
||||
for (outline = outline->next; outline != NULL; outline = outline->next) {
|
||||
box += outline->bounding_box();
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
void TBLOB::plot(ScrollView* window, ScrollView::Color color,
|
||||
ScrollView::Color child_color) {
|
||||
for (TESSLINE* outline = outlines; outline != NULL; outline = outline->next)
|
||||
outline->plot(window, color, child_color);
|
||||
}
|
||||
|
||||
// Factory to build a TWERD from a (C_BLOB) WERD, with polygonal
|
||||
// approximation along the way.
|
||||
TWERD* TWERD::PolygonalCopy(WERD* src) {
|
||||
TWERD* tessword = new TWERD;
|
||||
tessword->latin_script = src->flag(W_SCRIPT_IS_LATIN);
|
||||
C_BLOB_IT b_it(src->cblob_list());
|
||||
TBLOB *tail = NULL;
|
||||
for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
|
||||
C_BLOB* blob = b_it.data();
|
||||
TBLOB* tblob = TBLOB::PolygonalCopy(blob);
|
||||
if (tail == NULL) {
|
||||
tessword->blobs = tblob;
|
||||
} else {
|
||||
tail->next = tblob;
|
||||
}
|
||||
tail = tblob;
|
||||
}
|
||||
return tessword;
|
||||
}
|
||||
|
||||
// Normalize in-place and record the normalization in the DENORM.
|
||||
void TWERD::SetupBLNormalize(const BLOCK* block, const ROW* row,
|
||||
float x_height, bool numeric_mode,
|
||||
DENORM* denorm) const {
|
||||
int num_segments = 0;
|
||||
DENORM_SEG* segs = NULL;
|
||||
if (numeric_mode) {
|
||||
segs = new DENORM_SEG[NumBlobs()];
|
||||
for (TBLOB* blob = blobs; blob != NULL; blob = blob->next) {
|
||||
TBOX blob_box = blob->bounding_box();
|
||||
float factor = kBlnXHeight / x_height;
|
||||
factor = ClipToRange(kBlnXHeight * 4.0f / (3 * blob_box.height()),
|
||||
factor, factor * 1.5f);
|
||||
segs[num_segments].xstart = blob_box.left();
|
||||
segs[num_segments].ycoord = blob_box.bottom();
|
||||
segs[num_segments++].scale_factor = factor;
|
||||
}
|
||||
}
|
||||
denorm->SetupBLNormalize(block, row, x_height, bounding_box(),
|
||||
num_segments, segs);
|
||||
delete [] segs;
|
||||
}
|
||||
|
||||
// Normalize in-place using the DENORM.
|
||||
void TWERD::Normalize(const DENORM& denorm) {
|
||||
for (TBLOB* blob = blobs; blob != NULL; blob = blob->next) {
|
||||
blob->Normalize(denorm);
|
||||
}
|
||||
}
|
||||
|
||||
// Copies the data and the blobs, but leaves next untouched.
|
||||
void TWERD::CopyFrom(const TWERD& src) {
|
||||
Clear();
|
||||
latin_script = src.latin_script;
|
||||
TBLOB* prev_blob = NULL;
|
||||
for (TBLOB* srcblob = src.blobs; srcblob != NULL; srcblob = srcblob->next) {
|
||||
TBLOB* new_blob = new TBLOB(*srcblob);
|
||||
if (blobs == NULL)
|
||||
blobs = new_blob;
|
||||
else
|
||||
prev_blob->next = new_blob;
|
||||
prev_blob = new_blob;
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes owned data.
|
||||
void TWERD::Clear() {
|
||||
for (TBLOB* next_blob = NULL; blobs != NULL; blobs = next_blob) {
|
||||
next_blob = blobs->next;
|
||||
delete blobs;
|
||||
}
|
||||
}
|
||||
|
||||
// Recomputes the bounding boxes of the blobs.
|
||||
void TWERD::ComputeBoundingBoxes() {
|
||||
for (TBLOB* blob = blobs; blob != NULL; blob = blob->next) {
|
||||
blob->ComputeBoundingBoxes();
|
||||
}
|
||||
}
|
||||
|
||||
TBOX TWERD::bounding_box() const {
|
||||
TBOX result;
|
||||
for (TBLOB* blob = blobs; blob != NULL; blob = blob->next) {
|
||||
TBOX box = blob->bounding_box();
|
||||
result += box;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Merges the blobs from start to end, not including end, and deletes
|
||||
// the blobs between start and end.
|
||||
void TWERD::MergeBlobs(int start, int end) {
|
||||
TBLOB* blob = blobs;
|
||||
for (int i = 0; i < start && blob != NULL; ++i)
|
||||
blob = blob->next;
|
||||
if (blob == NULL || blob->next == NULL)
|
||||
return;
|
||||
TBLOB* next_blob = blob->next;
|
||||
TESSLINE* outline = blob->outlines;
|
||||
for (int i = start + 1; i < end && next_blob != NULL; ++i) {
|
||||
// Take the outlines from the next blob.
|
||||
if (outline == NULL) {
|
||||
blob->outlines = next_blob->outlines;
|
||||
outline = blob->outlines;
|
||||
} else {
|
||||
while (outline->next != NULL)
|
||||
outline = outline->next;
|
||||
outline->next = next_blob->outlines;
|
||||
next_blob->outlines = NULL;
|
||||
}
|
||||
// Delete the next blob and move on.
|
||||
TBLOB* dead_blob = next_blob;
|
||||
next_blob = next_blob->next;
|
||||
blob->next = next_blob;
|
||||
delete dead_blob;
|
||||
}
|
||||
}
|
||||
|
||||
void TWERD::plot(ScrollView* window) {
|
||||
ScrollView::Color color = WERD::NextColor(ScrollView::BLACK);
|
||||
for (TBLOB* blob = blobs; blob != NULL; blob = blob->next) {
|
||||
blob->plot(window, color, ScrollView::BROWN);
|
||||
color = WERD::NextColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* 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 */
|
||||
TBOX bbox = blob->bounding_box();
|
||||
*origin = (bbox.topleft() + bbox.botright()) / 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;
|
||||
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;
|
||||
|
||||
TBOX bbox = blobs->bounding_box();
|
||||
width_record->widths[i++] = bbox.width();
|
||||
/* First width */
|
||||
blob_end = bbox.right();
|
||||
|
||||
for (TBLOB* blob = blobs->next; blob != NULL; blob = blob->next) {
|
||||
TBOX curbox = blob->bounding_box();
|
||||
width_record->widths[i++] = curbox.left() - blob_end;
|
||||
width_record->widths[i++] = curbox.width();
|
||||
blob_end = curbox.right();
|
||||
}
|
||||
return width_record;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* count_blobs
|
||||
*
|
||||
* Return a count of the number of blobs attached to this one.
|
||||
**********************************************************************/
|
||||
int count_blobs(TBLOB *blobs) {
|
||||
int x = 0;
|
||||
|
||||
for (TBLOB* b = blobs; b != NULL; b = b->next)
|
||||
x++;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* divisible_blob
|
||||
*
|
||||
* Returns true if the blob contains multiple outlines than can be
|
||||
* separated using divide_blobs. Sets the location to be used in the
|
||||
* call to divide_blobs.
|
||||
**********************************************************************/
|
||||
bool divisible_blob(TBLOB *blob, bool italic_blob, TPOINT* location) {
|
||||
if (blob->outlines == NULL || blob->outlines->next == NULL)
|
||||
return false; // Need at least 2 outlines for it to be possible.
|
||||
int max_gap = 0;
|
||||
TPOINT vertical = italic_blob ? kDivisibleVerticalItalic
|
||||
: kDivisibleVerticalUpright;
|
||||
for (TESSLINE* outline1 = blob->outlines; outline1 != NULL;
|
||||
outline1 = outline1->next) {
|
||||
if (outline1->is_hole)
|
||||
continue; // Holes do not count as separable.
|
||||
TPOINT mid_pt1(
|
||||
static_cast<inT16>((outline1->topleft.x + outline1->botright.x) / 2),
|
||||
static_cast<inT16>((outline1->topleft.y + outline1->botright.y) / 2));
|
||||
int mid_prod1 = CROSS(mid_pt1, vertical);
|
||||
int min_prod1, max_prod1;
|
||||
outline1->MinMaxCrossProduct(vertical, &min_prod1, &max_prod1);
|
||||
for (TESSLINE* outline2 = outline1->next; outline2 != NULL;
|
||||
outline2 = outline2->next) {
|
||||
if (outline2->is_hole)
|
||||
continue; // Holes do not count as separable.
|
||||
TPOINT mid_pt2(
|
||||
static_cast<inT16>((outline2->topleft.x + outline2->botright.x) / 2),
|
||||
static_cast<inT16>((outline2->topleft.y + outline2->botright.y) / 2));
|
||||
int mid_prod2 = CROSS(mid_pt2, vertical);
|
||||
int min_prod2, max_prod2;
|
||||
outline2->MinMaxCrossProduct(vertical, &min_prod2, &max_prod2);
|
||||
int mid_gap = abs(mid_prod2 - mid_prod1);
|
||||
int overlap = MIN(max_prod1, max_prod2) - MAX(min_prod1, min_prod2);
|
||||
if (mid_gap - overlap / 4 > max_gap) {
|
||||
max_gap = mid_gap - overlap / 4;
|
||||
*location = mid_pt1;
|
||||
*location += mid_pt2;
|
||||
*location /= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Use the y component of the vertical vector as an approximation to its
|
||||
// length.
|
||||
return max_gap > vertical.y;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* 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, bool italic_blob,
|
||||
const TPOINT& location) {
|
||||
TPOINT vertical = italic_blob ? kDivisibleVerticalItalic
|
||||
: kDivisibleVerticalUpright;
|
||||
TESSLINE *outline1 = NULL;
|
||||
TESSLINE *outline2 = NULL;
|
||||
|
||||
TESSLINE *outline = blob->outlines;
|
||||
blob->outlines = NULL;
|
||||
int location_prod = CROSS(location, vertical);
|
||||
|
||||
while (outline != NULL) {
|
||||
TPOINT mid_pt(
|
||||
static_cast<inT16>((outline->topleft.x + outline->botright.x) / 2),
|
||||
static_cast<inT16>((outline->topleft.y + outline->botright.y) / 2));
|
||||
int mid_prod = CROSS(mid_pt, vertical);
|
||||
if (mid_prod < location_prod) {
|
||||
// Outline is in left blob.
|
||||
if (outline1)
|
||||
outline1->next = outline;
|
||||
else
|
||||
blob->outlines = outline;
|
||||
outline1 = outline;
|
||||
} else {
|
||||
// Outline is in right 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;
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright 2008 Google Inc. All Rights Reserved.
|
||||
// Author: scharron@google.com (Samuel Charron)
|
||||
|
||||
#include "ccutil.h"
|
||||
|
||||
namespace tesseract {
|
||||
CCUtil::CCUtil() :
|
||||
params_(),
|
||||
STRING_INIT_MEMBER(m_data_sub_dir,
|
||||
"tessdata/", "Directory for data files", ¶ms_),
|
||||
#ifdef _WIN32
|
||||
STRING_INIT_MEMBER(tessedit_module_name, WINDLLNAME,
|
||||
"Module colocated with tessdata dir", ¶ms_),
|
||||
#endif
|
||||
INT_INIT_MEMBER(ambigs_debug_level, 0, "Debug level for unichar ambiguities",
|
||||
¶ms_),
|
||||
BOOL_MEMBER(use_definite_ambigs_for_classifier, 0, "Use definite"
|
||||
" ambiguities when running character classifier", ¶ms_),
|
||||
BOOL_MEMBER(use_ambigs_for_adaption, 0, "Use ambigs for deciding"
|
||||
" whether to adapt to a character", ¶ms_) {
|
||||
}
|
||||
|
||||
CCUtil::~CCUtil() {
|
||||
}
|
||||
|
||||
|
||||
CCUtilMutex::CCUtilMutex() {
|
||||
#ifdef _WIN32
|
||||
mutex_ = CreateMutex(0, FALSE, 0);
|
||||
#else
|
||||
pthread_mutex_init(&mutex_, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CCUtilMutex::Lock() {
|
||||
#ifdef _WIN32
|
||||
WaitForSingleObject(mutex_, INFINITE);
|
||||
#else
|
||||
pthread_mutex_lock(&mutex_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CCUtilMutex::Unlock() {
|
||||
#ifdef _WIN32
|
||||
ReleaseMutex(mutex_);
|
||||
#else
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
#endif
|
||||
}
|
||||
|
||||
CCUtilMutex tprintfMutex; // should remain global
|
||||
} // namespace tesseract
|
|
@ -1,178 +0,0 @@
|
|||
/* -*-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 "callcpp.h"
|
||||
|
||||
#ifdef __UNIX__
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
V a r i a b l e s
|
||||
----------------------------------------------------------------------*/
|
||||
BOOL_VAR(wordrec_display_splits, 0, "Display splits");
|
||||
|
||||
makestructure(newsplit, free_split, SPLIT);
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
F u n c t i o n s
|
||||
----------------------------------------------------------------------*/
|
||||
|
||||
/**********************************************************************
|
||||
* 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 = new EDGEPT;
|
||||
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;
|
||||
|
||||
return (this_edgept);
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* remove_edgept
|
||||
*
|
||||
* Remove a given EDGEPT from its list and delete it.
|
||||
**********************************************************************/
|
||||
void remove_edgept(EDGEPT *point) {
|
||||
EDGEPT *prev = point->prev;
|
||||
EDGEPT *next = point->next;
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
prev->vec.x = next->pos.x - prev->pos.x;
|
||||
prev->vec.y = next->pos.y - prev->pos.y;
|
||||
delete point;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* 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;
|
||||
|
||||
delete tmp1;
|
||||
delete 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;
|
||||
}
|
|
@ -1,815 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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 <assert.h>
|
||||
#endif
|
||||
#include "stderr.h"
|
||||
#include "globaloc.h"
|
||||
#include "blread.h"
|
||||
#include "blobbox.h"
|
||||
#include "ccstruct.h"
|
||||
#include "edgblob.h"
|
||||
#include "drawtord.h"
|
||||
#include "makerow.h"
|
||||
#include "wordseg.h"
|
||||
#include "imgs.h"
|
||||
#include "textord.h"
|
||||
#include "tordmain.h"
|
||||
#include "secname.h"
|
||||
|
||||
// Include automatically generated configuration file if running autoconf.
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config_auto.h"
|
||||
#endif
|
||||
|
||||
#include "allheaders.h"
|
||||
|
||||
const ERRCODE BLOCKLESS_BLOBS = "Warning:some blobs assigned to no block";
|
||||
|
||||
#undef EXTERN
|
||||
#define EXTERN
|
||||
|
||||
#define MAX_NEAREST_DIST 600 //for block skew stats
|
||||
|
||||
/**********************************************************************
|
||||
* SetBlobStrokeWidth
|
||||
*
|
||||
* Set the horizontal and vertical stroke widths in the blob.
|
||||
**********************************************************************/
|
||||
void SetBlobStrokeWidth(Pix* pix, BLOBNBOX* blob) {
|
||||
// Cut the blob rectangle into a Pix.
|
||||
int pix_height = pixGetHeight(pix);
|
||||
const TBOX& box = blob->bounding_box();
|
||||
int width = box.width();
|
||||
int height = box.height();
|
||||
Box* blob_pix_box = boxCreate(box.left(), pix_height - box.top(),
|
||||
width, height);
|
||||
Pix* pix_blob = pixClipRectangle(pix, blob_pix_box, NULL);
|
||||
boxDestroy(&blob_pix_box);
|
||||
Pix* dist_pix = pixDistanceFunction(pix_blob, 4, 8, L_BOUNDARY_BG);
|
||||
pixDestroy(&pix_blob);
|
||||
// Compute the stroke widths.
|
||||
uinT32* data = pixGetData(dist_pix);
|
||||
int wpl = pixGetWpl(dist_pix);
|
||||
// Horizontal width of stroke.
|
||||
STATS h_stats(0, width + 1);
|
||||
for (int y = 0; y < height; ++y) {
|
||||
uinT32* pixels = data + y*wpl;
|
||||
int prev_pixel = 0;
|
||||
int pixel = GET_DATA_BYTE(pixels, 0);
|
||||
for (int x = 1; x < width; ++x) {
|
||||
int next_pixel = GET_DATA_BYTE(pixels, x);
|
||||
// We are looking for a pixel that is equal to its vertical neighbours,
|
||||
// yet greater than its left neighbour.
|
||||
if (prev_pixel < pixel &&
|
||||
(y == 0 || pixel == GET_DATA_BYTE(pixels - wpl, x - 1)) &&
|
||||
(y == height - 1 || pixel == GET_DATA_BYTE(pixels + wpl, x - 1))) {
|
||||
if (pixel > next_pixel) {
|
||||
// Single local max, so an odd width.
|
||||
h_stats.add(pixel * 2 - 1, 1);
|
||||
} else if (pixel == next_pixel && x + 1 < width &&
|
||||
pixel > GET_DATA_BYTE(pixels, x + 1)) {
|
||||
// Double local max, so an even width.
|
||||
h_stats.add(pixel * 2, 1);
|
||||
}
|
||||
}
|
||||
prev_pixel = pixel;
|
||||
pixel = next_pixel;
|
||||
}
|
||||
}
|
||||
// Vertical width of stroke.
|
||||
STATS v_stats(0, height + 1);
|
||||
for (int x = 0; x < width; ++x) {
|
||||
int prev_pixel = 0;
|
||||
int pixel = GET_DATA_BYTE(data, x);
|
||||
for (int y = 1; y < height; ++y) {
|
||||
uinT32* pixels = data + y*wpl;
|
||||
int next_pixel = GET_DATA_BYTE(pixels, x);
|
||||
// We are looking for a pixel that is equal to its horizontal neighbours,
|
||||
// yet greater than its upper neighbour.
|
||||
if (prev_pixel < pixel &&
|
||||
(x == 0 || pixel == GET_DATA_BYTE(pixels - wpl, x - 1)) &&
|
||||
(x == width - 1 || pixel == GET_DATA_BYTE(pixels - wpl, x + 1))) {
|
||||
if (pixel > next_pixel) {
|
||||
// Single local max, so an odd width.
|
||||
v_stats.add(pixel * 2 - 1, 1);
|
||||
} else if (pixel == next_pixel && y + 1 < height &&
|
||||
pixel > GET_DATA_BYTE(pixels + wpl, x)) {
|
||||
// Double local max, so an even width.
|
||||
v_stats.add(pixel * 2, 1);
|
||||
}
|
||||
}
|
||||
prev_pixel = pixel;
|
||||
pixel = next_pixel;
|
||||
}
|
||||
}
|
||||
pixDestroy(&dist_pix);
|
||||
// Store the horizontal and vertical width in the blob, keeping both
|
||||
// widths if there is enough information, otherwse only the one with
|
||||
// the most samples.
|
||||
// If there are insufficent samples, store zero, rather than using
|
||||
// 2*area/perimeter, as the numbers that gives do not match the numbers
|
||||
// from the distance method.
|
||||
if (h_stats.get_total() >= (width + height) / 4) {
|
||||
blob->set_horz_stroke_width(h_stats.ile(0.5f));
|
||||
if (v_stats.get_total() >= (width + height) / 4)
|
||||
blob->set_vert_stroke_width(v_stats.ile(0.5f));
|
||||
else
|
||||
blob->set_vert_stroke_width(0.0f);
|
||||
} else {
|
||||
if (v_stats.get_total() >= (width + height) / 4 ||
|
||||
v_stats.get_total() > h_stats.get_total()) {
|
||||
blob->set_horz_stroke_width(0.0f);
|
||||
blob->set_vert_stroke_width(v_stats.ile(0.5f));
|
||||
} else {
|
||||
blob->set_horz_stroke_width(h_stats.get_total() > 2 ? h_stats.ile(0.5f)
|
||||
: 0.0f);
|
||||
blob->set_vert_stroke_width(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* assign_blobs_to_blocks2
|
||||
*
|
||||
* Make a list of TO_BLOCKs for portrait and landscape orientation.
|
||||
**********************************************************************/
|
||||
|
||||
void assign_blobs_to_blocks2(Pix* pix,
|
||||
BLOCK_LIST *blocks, // blocks to process
|
||||
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();
|
||||
port_block = new TO_BLOCK(block);
|
||||
|
||||
// Convert the good outlines to block->blob_list
|
||||
port_box_it.set_to_list(&port_block->blobs);
|
||||
blob_it.set_to_list(block->blob_list());
|
||||
for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
|
||||
blob = blob_it.extract();
|
||||
newblob = new BLOBNBOX(blob); // Convert blob to BLOBNBOX.
|
||||
SetBlobStrokeWidth(pix, newblob);
|
||||
port_box_it.add_after_then_move(newblob);
|
||||
}
|
||||
|
||||
// Put the rejected outlines in block->noise_blobs, which allows them to
|
||||
// be reconsidered and sorted back into rows and recover outlines mistakenly
|
||||
// rejected.
|
||||
port_box_it.set_to_list(&port_block->noise_blobs);
|
||||
blob_it.set_to_list(block->reject_blobs());
|
||||
for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
|
||||
blob = blob_it.extract();
|
||||
newblob = new BLOBNBOX(blob); // Convert blob to BLOBNBOX.
|
||||
SetBlobStrokeWidth(pix, newblob);
|
||||
port_box_it.add_after_then_move(newblob);
|
||||
}
|
||||
|
||||
port_block_it.add_after_then_move(port_block);
|
||||
}
|
||||
}
|
||||
|
||||
namespace tesseract {
|
||||
/**********************************************************************
|
||||
* find_components
|
||||
*
|
||||
* Find the C_OUTLINEs of the connected components in each block, put them
|
||||
* in C_BLOBs, and filter them by size, putting the different size
|
||||
* grades on different lists in the matching TO_BLOCK in to_blocks.
|
||||
**********************************************************************/
|
||||
|
||||
void Textord::find_components(Pix* pix, BLOCK_LIST *blocks,
|
||||
TO_BLOCK_LIST *to_blocks) {
|
||||
int width = pixGetWidth(pix);
|
||||
int height = pixGetHeight(pix);
|
||||
if (width > MAX_INT16 || height > MAX_INT16) {
|
||||
tprintf("Input image too large! (%d, %d)\n", width, height);
|
||||
return; // Can't handle it.
|
||||
}
|
||||
|
||||
set_global_loc_code(LOC_EDGE_PROG);
|
||||
|
||||
BLOCK_IT block_it(blocks); // iterator
|
||||
for (block_it.mark_cycle_pt(); !block_it.cycled_list();
|
||||
block_it.forward()) {
|
||||
BLOCK* block = block_it.data();
|
||||
if (block->poly_block() == NULL || block->poly_block()->IsText()) {
|
||||
extract_edges(pix, block);
|
||||
}
|
||||
}
|
||||
|
||||
assign_blobs_to_blocks2(pix, blocks, to_blocks);
|
||||
ICOORD page_tr(width, height);
|
||||
filter_blobs(page_tr, to_blocks, !textord_test_landscape);
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* filter_blobs
|
||||
*
|
||||
* Sort the blobs into sizes in all the blocks for later work.
|
||||
**********************************************************************/
|
||||
|
||||
void Textord::filter_blobs(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
|
||||
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
if (to_win != NULL)
|
||||
to_win->Clear();
|
||||
#endif // GRAPHICS_DISABLED
|
||||
|
||||
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 *
|
||||
(tesseract::CCStruct::kDescenderFraction +
|
||||
tesseract::CCStruct::kXHeightFraction +
|
||||
2 * tesseract::CCStruct::kAscenderFraction) /
|
||||
tesseract::CCStruct::kXHeightFraction;
|
||||
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 == NULL)
|
||||
create_to_win(page_tr);
|
||||
block->plot_graded_blobs(to_win);
|
||||
}
|
||||
if (textord_show_boxes && testing_on) {
|
||||
if (to_win == NULL)
|
||||
create_to_win(page_tr);
|
||||
plot_box_list(to_win, &block->noise_blobs, ScrollView::WHITE);
|
||||
plot_box_list(to_win, &block->small_blobs, ScrollView::WHITE);
|
||||
plot_box_list(to_win, &block->large_blobs, ScrollView::WHITE);
|
||||
plot_box_list(to_win, &block->blobs, ScrollView::WHITE);
|
||||
}
|
||||
#endif // GRAPHICS_DISABLED
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* filter_noise_blobs
|
||||
*
|
||||
* Move small blobs to a separate list.
|
||||
**********************************************************************/
|
||||
|
||||
float Textord::filter_noise_blobs(
|
||||
BLOBNBOX_LIST *src_list, // original 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 *
|
||||
(tesseract::CCStruct::kDescenderFraction +
|
||||
tesseract::CCStruct::kXHeightFraction +
|
||||
2 * tesseract::CCStruct::kAscenderFraction) /
|
||||
tesseract::CCStruct::kXHeightFraction);
|
||||
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 > max_y)
|
||||
large_it.add_after_then_move(small_it.extract ());
|
||||
else if (height >= min_y)
|
||||
src_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 *= tesseract::CCStruct::kXHeightCapRatio;
|
||||
if (max_height > initial_x)
|
||||
initial_x = max_height;
|
||||
// printf(" ret=%g\n",initial_x);
|
||||
return initial_x;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* cleanup_blocks
|
||||
*
|
||||
* Delete empty blocks, rows from the page.
|
||||
**********************************************************************/
|
||||
|
||||
void Textord::cleanup_blocks( //remove empties
|
||||
BLOCK_LIST *blocks //list
|
||||
) {
|
||||
BLOCK_IT block_it = blocks; //iterator
|
||||
ROW_IT row_it; //row iterator
|
||||
|
||||
int num_rows = 0;
|
||||
int num_rows_all = 0;
|
||||
int num_blocks = 0;
|
||||
int num_blocks_all = 0;
|
||||
for (block_it.mark_cycle_pt (); !block_it.cycled_list ();
|
||||
block_it.forward ()) {
|
||||
num_rows = 0;
|
||||
num_rows_all = 0;
|
||||
row_it.set_to_list (block_it.data ()->row_list ());
|
||||
for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
|
||||
++num_rows_all;
|
||||
clean_small_noise_from_words(row_it.data());
|
||||
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(),
|
||||
textord_blshift_maxshift,
|
||||
textord_blshift_xfraction);
|
||||
++num_rows;
|
||||
}
|
||||
}
|
||||
if (block_it.data()->row_list()->empty() &&
|
||||
(block_it.data()->poly_block() == NULL ||
|
||||
block_it.data()->poly_block()->IsText())) {
|
||||
delete block_it.extract(); // Lose empty text blocks but not other types.
|
||||
} else {
|
||||
++num_blocks;
|
||||
}
|
||||
++num_blocks_all;
|
||||
if (textord_noise_debug)
|
||||
tprintf("cleanup_blocks: # rows = %d / %d\n", num_rows, num_rows_all);
|
||||
}
|
||||
if (textord_noise_debug)
|
||||
tprintf("cleanup_blocks: # blocks = %d / %d\n", num_blocks, num_blocks_all);
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* clean_noise_from_row
|
||||
*
|
||||
* Move blobs of words from rows of garbage into the reject blobs list.
|
||||
**********************************************************************/
|
||||
|
||||
BOOL8 Textord::clean_noise_from_row( //remove empties
|
||||
ROW *row //row to clean
|
||||
) {
|
||||
BOOL8 testing_on;
|
||||
TBOX 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 Textord::clean_noise_from_words( //remove empties
|
||||
ROW *row //row to clean
|
||||
) {
|
||||
TBOX 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 || textord_no_rejects)
|
||||
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);
|
||||
}
|
||||
|
||||
// Remove outlines that are a tiny fraction in either width or height
|
||||
// of the word height.
|
||||
void Textord::clean_small_noise_from_words(ROW *row) {
|
||||
WERD_IT word_it(row->word_list());
|
||||
for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) {
|
||||
WERD* word = word_it.data();
|
||||
int min_size = static_cast<int>(
|
||||
textord_noise_hfract * word->bounding_box().height() + 0.5);
|
||||
C_BLOB_IT blob_it(word->cblob_list());
|
||||
for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
|
||||
C_BLOB* blob = blob_it.data();
|
||||
C_OUTLINE_IT out_it(blob->out_list());
|
||||
for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) {
|
||||
C_OUTLINE* outline = out_it.data();
|
||||
outline->RemoveSmallRecursive(min_size, &out_it);
|
||||
}
|
||||
if (blob->out_list()->empty()) {
|
||||
delete blob_it.extract();
|
||||
}
|
||||
}
|
||||
if (word->cblob_list()->empty()) {
|
||||
if (!word_it.at_last()) {
|
||||
// The next word is no longer a fuzzy non space if it was before,
|
||||
// since the word before is about to be deleted.
|
||||
WERD* next_word = word_it.data_relative(1);
|
||||
if (next_word->flag(W_FUZZY_NON)) {
|
||||
next_word->set_flag(W_FUZZY_NON, false);
|
||||
}
|
||||
}
|
||||
delete word_it.extract();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // tesseract
|
||||
|
||||
/**********************************************************************
|
||||
* tweak_row_baseline
|
||||
*
|
||||
* Shift baseline to fit the blobs more accurately where they are
|
||||
* close enough.
|
||||
**********************************************************************/
|
||||
|
||||
void tweak_row_baseline(ROW *row,
|
||||
double blshift_maxshift,
|
||||
double blshift_xfraction) {
|
||||
TBOX 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 < blshift_maxshift
|
||||
&& blob_box.height () / row->x_height () > 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;
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
How to run UNLV tests.
|
||||
|
||||
The scripts in this directory make it possible to duplicate the tests
|
||||
published in the Fourth Annual Test of OCR Accuracy.
|
||||
See http://www.isri.unlv.edu/downloads/AT-1995.pdf
|
||||
but first you have to get the tools and data from UNLV:
|
||||
|
||||
Step 1: to download the images goto
|
||||
http://www.isri.unlv.edu/ISRI/OCRtk
|
||||
and get 3b.tgz, Bb.tgz, Mb.tgz and Nb.tgz.
|
||||
|
||||
Step 2: extract the files. It doesn't really matter where
|
||||
in your filesystem you put them, but they must go under a common
|
||||
root so you have directories 3, B, M and N in, for example,
|
||||
/users/me/ISRI-OCRtk.
|
||||
|
||||
Step 3: Reorg the files
|
||||
The lack of tif extensions on the images is inconvenient, so there
|
||||
is a script to reorganize the data to match the rest of the test
|
||||
scripts.
|
||||
cd to /users/me/ISRI-OCRtk or wherever 3, B, M and N ended up and run
|
||||
/blah/blah/tesseract-ocr/testing/reorgdata.sh 3B
|
||||
This makes directories doe3.3B, bus.3B, mag.3B and news.3B.
|
||||
You can now get rid of 3, B, M, and N unless you want to get some of the
|
||||
other scanning resolutions out of them.
|
||||
|
||||
Step 4: Download the ISRI toolkit from:
|
||||
http://www.isri.unlv.edu/downloads/ftk-1.0.tgz
|
||||
|
||||
Step 5: If they work for you, use the binaries directly from the bin
|
||||
directory and put them in tesseract-ocr/testing/unlv
|
||||
otherwise build the tools for yourself and put them there.
|
||||
|
||||
Step 6: cd back to your main tesseract-ocr dir and Build tesseract.
|
||||
|
||||
Step 7: run testing/runalltests.sh with the root data dir and testname:
|
||||
testing/runalltests.sh /users/me/ISRI-OCRtk tess2.0
|
||||
and go to the gym, have lunch etc.
|
||||
|
||||
Step 8: There should be a file
|
||||
testing/reports/tess2.0.summary that contains the final summarized accuracy
|
||||
report and comparison with the 1995 results.
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
Note that this is a text-only and possibly out-of-date version of the
|
||||
wiki ReadMe, which is located at:
|
||||
http://code.google.com/p/tesseract-ocr/wiki/ReadMe
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
This package contains the Tesseract Open Source OCR Engine.
|
||||
Originally 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.
|
||||
|
||||
|
||||
Dependencies and Licenses
|
||||
=========================
|
||||
|
||||
Leptonica is required. (www.leptonica.com). Tesseract no longer compiles
|
||||
without Leptonica.
|
||||
|
||||
|
||||
Installing and Running Tesseract
|
||||
--------------------------------
|
||||
|
||||
All Users Do NOT Ignore!
|
||||
The tarballs are split into pieces.
|
||||
|
||||
tesseract-x.xx.tar.gz contains all the source code.
|
||||
|
||||
tesseract-x.xx.<lang>.tar.gz contains the language data files for <lang>.
|
||||
You need at least one of these or Tesseract will not work.
|
||||
|
||||
Note that tesseract-x.xx.tar.gz unpacks to the tesseract-ocr directory.
|
||||
tesseract-x.xx.<lang>.tar.gz unpacks to the tessdata directory which
|
||||
belongs inside your tesseract-ocr directory. It is therefore best to
|
||||
download them into your tesseract-x.xx directory, so you can use unpack
|
||||
here or equivalent. You can unpack as many of the language packs as you
|
||||
care to, as they all contain different files. Note that if you are using
|
||||
make install you should unpack your language data to your source tree
|
||||
before you run make install. If you unpack them as root to the
|
||||
destination directory of make install, then the user ids and access
|
||||
permissions might be messed up.
|
||||
|
||||
boxtiff-2.xx.<lang>.tar.gz contains data that was used in training for
|
||||
those that want to do their own training. Most users should NOT download
|
||||
these files.
|
||||
|
||||
Instructions for using the training tools are documented separately at
|
||||
Tesseract wiki http://code.google.com/p/tesseract-ocr/w/list
|
||||
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
Please use installer (for 3.00 and above). Tesseract is library with
|
||||
command line interface. If you need GUI, please check AddOns wiki page
|
||||
http://code.google.com/p/tesseract-ocr/wiki/AddOns#GUI
|
||||
|
||||
If you are building from the sources, the recommended build platform is
|
||||
VC++ Express 2008 (optionally 2010).
|
||||
|
||||
The executables are built with static linking, so they stand more chance
|
||||
of working out of the box on more windows systems.
|
||||
|
||||
The executable must reside in the same directory as the tessdata
|
||||
directory or you need to set up environment variable TESSDATA_PREFIX.
|
||||
Installer will set it up for you.
|
||||
|
||||
The command line is:
|
||||
|
||||
tesseract imagename outputbase [-l lang] [-psm pagesegmode] [configfiles...]
|
||||
|
||||
If you need interface to other applications, please check wrapper section
|
||||
on AddOns wiki page:
|
||||
http://code.google.com/p/tesseract-ocr/wiki/AddOns#Tesseract_3.0x
|
||||
|
||||
|
||||
Non-Windows (or Cygwin)
|
||||
-----------------------
|
||||
|
||||
You have to tell Tesseract through a standard unix mechanism where to
|
||||
find its data directory. You must either:
|
||||
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
|
||||
to move the data files to the standard place, or:
|
||||
|
||||
export TESSDATA_PREFIX="directory in which your tessdata resides/"
|
||||
|
||||
In either case the command line is:
|
||||
|
||||
tesseract imagename outputbase [-l lang] [-psm pagesegmode] [configfiles...]
|
||||
|
||||
New there is a tesseract.spec for making rpms. (Thanks to Andrew Ziem for
|
||||
the help.) It might work with your OS if you know how to do that.
|
||||
|
||||
If you are linking to the libraries, as Ocropus does, please link to
|
||||
libtesseract_api.
|
||||
|
||||
|
||||
|
||||
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 gcc4.4.3 and under Windows
|
||||
with VC++2008. The C++ code makes heavy use of a list system using macros.
|
||||
This predates stl, was portable before stl, and is more efficient than stl
|
||||
lists, but has the big negative that if you do get a segmentation violation,
|
||||
it is hard to debug.
|
||||
|
||||
The most recent change is that Tesseract can now recognize 39 languages,
|
||||
including Arabic, Hindi, Vietnamese, plus 3 Fraktur variants
|
||||
is fully UTF8 capable, and is fully trainable. See TrainingTesseract for
|
||||
more information on training.
|
||||
|
||||
Tesseract was included in UNLV's Fourth Annual Test of OCR Accuracy.
|
||||
Results were available on http://www.isri.unlv.edu/downloads/AT-1995.pdf.
|
||||
With Tesseract 2.00, scripts were included to allow anyone to reproduce
|
||||
some of these tests. See TestingTesseract for more details.
|
||||
|
||||
|
||||
About the Engine
|
||||
================
|
||||
This code is a raw OCR engine. It has limited PAGE LAYOUT ANALYSIS, simple
|
||||
OUTPUT FORMATTING (txt, hocr/html), and NO UI.
|
||||
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.
|
||||
As of 3.01, Tesseract is fully unicode (UTF-8) enabled, and can recognize 39
|
||||
languages "out of the box." Code and documentation is provided for the brave
|
||||
to train in other languages. See code.google.com/p/tesseract-ocr for more
|
||||
information on training.
|
|
@ -1,134 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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 "params.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
|
|
@ -1,276 +0,0 @@
|
|||
/*
|
||||
* default.css_t
|
||||
* ~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- default theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 100%;
|
||||
background-color: #11303d;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.document {
|
||||
background-color: #1c4e63;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 230px;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
padding: 0 20px 30px 20px;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
color: #ffffff;
|
||||
width: 100%;
|
||||
padding: 9px 0 9px 0;
|
||||
text-align: center;
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #ffffff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.related {
|
||||
background-color: #133f52;
|
||||
line-height: 30px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div.related a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 {
|
||||
font-family: 'Trebuchet MS', sans-serif;
|
||||
color: #ffffff;
|
||||
font-size: 1.4em;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: 'Trebuchet MS', sans-serif;
|
||||
color: #ffffff;
|
||||
font-size: 1.3em;
|
||||
font-weight: normal;
|
||||
margin: 5px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.topless {
|
||||
margin: 5px 10px 10px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #98dbcc;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #98dbcc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
|
||||
/* for collapsible sidebar */
|
||||
div#sidebarbutton {
|
||||
background-color: #3c6e83;
|
||||
}
|
||||
|
||||
|
||||
/* -- hyperlink styles ------------------------------------------------------ */
|
||||
|
||||
a {
|
||||
color: #355f7c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #355f7c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
a.external {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dashed #355f7c;
|
||||
}
|
||||
|
||||
a.external:hover {
|
||||
text-decoration: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
a.external:visited {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dashed #355f7c;
|
||||
}
|
||||
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: 'Trebuchet MS', sans-serif;
|
||||
background-color: #f2f2f2;
|
||||
font-weight: normal;
|
||||
color: #20435c;
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin: 20px -20px 10px -20px;
|
||||
padding: 3px 0 3px 10px;
|
||||
}
|
||||
|
||||
div.body h1 { margin-top: 0; font-size: 200%; }
|
||||
div.body h2 { font-size: 160%; }
|
||||
div.body h3 { font-size: 140%; }
|
||||
div.body h4 { font-size: 120%; }
|
||||
div.body h5 { font-size: 110%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: #c60f0f;
|
||||
font-size: 0.8em;
|
||||
padding: 0 4px 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
background-color: #c60f0f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
text-align: justify;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title + p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.admonition p {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.admonition pre {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.admonition ul, div.admonition ol {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: #ffe4e4;
|
||||
border: 1px solid #f66;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 5px;
|
||||
background-color: #eeffcc;
|
||||
color: #333333;
|
||||
line-height: 120%;
|
||||
border: 1px solid #ac9;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
padding: 0 1px 0 1px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #ede;
|
||||
}
|
||||
|
||||
.warning tt {
|
||||
background: #efc2c2;
|
||||
}
|
||||
|
||||
.note tt {
|
||||
background: #d6d6d6;
|
||||
}
|
||||
|
||||
.viewcode-back {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
background-color: #f4debf;
|
||||
border-top: 1px solid #ac9;
|
||||
border-bottom: 1px solid #ac9;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
s/"\([^"]*\)"/“\1”/g
|
||||
s/`\([^`']*\)'/‘\1’/g
|
||||
s/ '\([^`']*\)' / ‘\1’ /g
|
||||
s/ '\([^`']*\)'$/ ‘\1’/g
|
||||
s/^'\([^`']*\)' /‘\1’ /g
|
||||
s/“”/""/g
|
||||
s/“/“[1m/g
|
||||
s/”/[0m”/g
|
||||
s/‘/‘[1m/g
|
||||
s/’/[0m’/g
|
|
@ -1,19 +0,0 @@
|
|||
AM_CPPFLAGS += -I$(top_srcdir)/ccutil
|
||||
|
||||
if VISIBILITY
|
||||
AM_CPPFLAGS += -DTESS_EXPORTS \
|
||||
-fvisibility=hidden -fvisibility-inlines-hidden
|
||||
endif
|
||||
|
||||
noinst_HEADERS = \
|
||||
scrollview.h svmnode.h svutil.h
|
||||
|
||||
if !USING_MULTIPLELIBS
|
||||
noinst_LTLIBRARIES = libtesseract_viewer.la
|
||||
else
|
||||
lib_LTLIBRARIES = libtesseract_viewer.la
|
||||
libtesseract_viewer_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION)
|
||||
endif
|
||||
|
||||
libtesseract_viewer_la_SOURCES = \
|
||||
scrollview.cpp svmnode.cpp svutil.cpp svpaint.cpp
|
|
@ -1,144 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: feature_chebyshev.cpp
|
||||
* Description: Implementation of the Chebyshev coefficients Feature Class
|
||||
* Author: Ahmad Abdulkader
|
||||
* Created: 2008
|
||||
*
|
||||
* (C) Copyright 2008, 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "feature_base.h"
|
||||
#include "feature_chebyshev.h"
|
||||
#include "cube_utils.h"
|
||||
#include "const.h"
|
||||
#include "char_samp.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
FeatureChebyshev::FeatureChebyshev(TuningParams *params)
|
||||
: FeatureBase(params) {
|
||||
}
|
||||
|
||||
FeatureChebyshev::~FeatureChebyshev() {
|
||||
}
|
||||
|
||||
// Render a visualization of the features to a CharSamp.
|
||||
// This is mainly used by visual-debuggers
|
||||
CharSamp *FeatureChebyshev::ComputeFeatureBitmap(CharSamp *char_samp) {
|
||||
return char_samp;
|
||||
}
|
||||
|
||||
// Compute Chebyshev coefficients for the specified vector
|
||||
void FeatureChebyshev::ChebyshevCoefficients(const vector<float> &input,
|
||||
int coeff_cnt, float *coeff) {
|
||||
// re-sample function
|
||||
int input_range = (input.size() - 1);
|
||||
vector<float> resamp(coeff_cnt);
|
||||
for (int samp_idx = 0; samp_idx < coeff_cnt; samp_idx++) {
|
||||
// compute sampling position
|
||||
float samp_pos = input_range *
|
||||
(1 + cos(M_PI * (samp_idx + 0.5) / coeff_cnt)) / 2;
|
||||
// interpolate
|
||||
int samp_start = static_cast<int>(samp_pos);
|
||||
int samp_end = static_cast<int>(samp_pos + 0.5);
|
||||
float func_delta = input[samp_end] - input[samp_start];
|
||||
resamp[samp_idx] = input[samp_start] +
|
||||
((samp_pos - samp_start) * func_delta);
|
||||
}
|
||||
// compute the coefficients
|
||||
float normalizer = 2.0 / coeff_cnt;
|
||||
for (int coeff_idx = 0; coeff_idx < coeff_cnt; coeff_idx++, coeff++) {
|
||||
double sum = 0.0;
|
||||
for (int samp_idx = 0; samp_idx < coeff_cnt; samp_idx++) {
|
||||
sum += resamp[samp_idx] * cos(M_PI * coeff_idx * (samp_idx + 0.5) /
|
||||
coeff_cnt);
|
||||
}
|
||||
(*coeff) = (normalizer * sum);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the features of a given CharSamp
|
||||
bool FeatureChebyshev::ComputeFeatures(CharSamp *char_samp, float *features) {
|
||||
return ComputeChebyshevCoefficients(char_samp, features);
|
||||
}
|
||||
|
||||
// Compute the Chebyshev coefficients of a given CharSamp
|
||||
bool FeatureChebyshev::ComputeChebyshevCoefficients(CharSamp *char_samp,
|
||||
float *features) {
|
||||
if (char_samp->NormBottom() <= 0) {
|
||||
return false;
|
||||
}
|
||||
unsigned char *raw_data = char_samp->RawData();
|
||||
int stride = char_samp->Stride();
|
||||
// compute the height of the word
|
||||
int word_hgt = (255 * (char_samp->Top() + char_samp->Height()) /
|
||||
char_samp->NormBottom());
|
||||
// compute left & right profiles
|
||||
vector<float> left_profile(word_hgt, 0.0);
|
||||
vector<float> right_profile(word_hgt, 0.0);
|
||||
unsigned char *line_data = raw_data;
|
||||
for (int y = 0; y < char_samp->Height(); y++, line_data += stride) {
|
||||
int min_x = char_samp->Width();
|
||||
int max_x = -1;
|
||||
for (int x = 0; x < char_samp->Width(); x++) {
|
||||
if (line_data[x] == 0) {
|
||||
UpdateRange(x, &min_x, &max_x);
|
||||
}
|
||||
}
|
||||
left_profile[char_samp->Top() + y] =
|
||||
1.0 * (min_x == char_samp->Width() ? 0 : (min_x + 1)) /
|
||||
char_samp->Width();
|
||||
right_profile[char_samp->Top() + y] =
|
||||
1.0 * (max_x == -1 ? 0 : char_samp->Width() - max_x) /
|
||||
char_samp->Width();
|
||||
}
|
||||
|
||||
// compute top and bottom profiles
|
||||
vector<float> top_profile(char_samp->Width(), 0);
|
||||
vector<float> bottom_profile(char_samp->Width(), 0);
|
||||
for (int x = 0; x < char_samp->Width(); x++) {
|
||||
int min_y = word_hgt;
|
||||
int max_y = -1;
|
||||
line_data = raw_data;
|
||||
for (int y = 0; y < char_samp->Height(); y++, line_data += stride) {
|
||||
if (line_data[x] == 0) {
|
||||
UpdateRange(y + char_samp->Top(), &min_y, &max_y);
|
||||
}
|
||||
}
|
||||
top_profile[x] = 1.0 * (min_y == word_hgt ? 0 : (min_y + 1)) / word_hgt;
|
||||
bottom_profile[x] = 1.0 * (max_y == -1 ? 0 : (word_hgt - max_y)) / word_hgt;
|
||||
}
|
||||
|
||||
// compute the chebyshev coefficients of each profile
|
||||
ChebyshevCoefficients(left_profile, kChebychevCoefficientCnt, features);
|
||||
ChebyshevCoefficients(top_profile, kChebychevCoefficientCnt,
|
||||
features + kChebychevCoefficientCnt);
|
||||
ChebyshevCoefficients(right_profile, kChebychevCoefficientCnt,
|
||||
features + (2 * kChebychevCoefficientCnt));
|
||||
ChebyshevCoefficients(bottom_profile, kChebychevCoefficientCnt,
|
||||
features + (3 * kChebychevCoefficientCnt));
|
||||
return true;
|
||||
}
|
||||
} // namespace tesseract
|
|
@ -1,746 +0,0 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="tesseract"
|
||||
ProjectGUID="{C76996CB-C4CB-4D89-9F67-F605DF129618}"
|
||||
RootNamespace="tesseract"
|
||||
TargetFrameworkVersion="196613"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
<Platform
|
||||
Name="x64"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="LIB_Debug|Win32"
|
||||
OutputDirectory="..\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
InheritedPropertySheets="..\include\tesseract_versionnumbers.vsprops"
|
||||
CharacterSet="2"
|
||||
EnableManagedIncrementalBuild="0"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""
|
||||
Outputs=""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\api;..\..\ccmain;..\..\ccutil;..\..\ccstruct;..\..\classify;..\..\cube;..\..\cutil;..\..\dict;..\..\image;..\..\neural_networks\runtime;..\..\textord;..\..\viewer;..\..\wordrec;.;..\..\..\include;..\..\..\include\leptonica;..\port"
|
||||
PreprocessorDefinitions="WIN32;_WINDOWS;_DEBUG;USE_STD_NAMESPACE"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="1"
|
||||
CompileAs="0"
|
||||
DisableSpecificWarnings="4244;4305;4018;4267;4996;4800;4005;4355;4099;4566"
|
||||
ShowIncludes="false"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="MYVERSION="$(LIBTESS_VERSION_R)""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib user32.lib zlib$(ZLIB_VERSION)-static-mtdll-debug.lib libpng$(LIBPNG_VERSION)-static-mtdll-debug.lib libjpeg$(LIBJPEG_VERSION)-static-mtdll-debug.lib giflib$(GIFLIB_VERSION)-static-mtdll-debug.lib libtiff$(LIBTIFF_VERSION)-static-mtdll-debug.lib liblept$(LIBLEPT_VERSION)-static-mtdll-debug.lib $(NOINHERIT)"
|
||||
OutputFile="$(OutDir)\$(ProjectName)d.exe"
|
||||
AdditionalLibraryDirectories="..\..\..\lib"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="LIB_Debug|x64"
|
||||
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
InheritedPropertySheets="..\include\tesseract_versionnumbers.vsprops"
|
||||
CharacterSet="2"
|
||||
EnableManagedIncrementalBuild="0"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""
|
||||
Outputs=""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TargetEnvironment="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\api;..\..\ccmain;..\..\ccutil;..\..\ccstruct;..\..\classify;..\..\cube;..\..\cutil;..\..\dict;..\..\image;..\..\neural_networks\runtime;..\..\textord;..\..\viewer;..\..\wordrec;.;..\..\..\include;..\..\..\include\leptonica;..\port"
|
||||
PreprocessorDefinitions="WIN32;_WINDOWS;_DEBUG;USE_STD_NAMESPACE"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="1"
|
||||
CompileAs="0"
|
||||
DisableSpecificWarnings="4244;4305;4018;4267;4996;4800;4005;4355;4099;4566"
|
||||
ShowIncludes="false"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="MYVERSION="$(LIBTESS_VERSION_R)""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib user32.lib zlib$(ZLIB_VERSION)-static-mtdll-debug.lib libpng$(LIBPNG_VERSION)-static-mtdll-debug.lib libjpeg$(LIBJPEG_VERSION)-static-mtdll-debug.lib giflib$(GIFLIB_VERSION)-static-mtdll-debug.lib libtiff$(LIBTIFF_VERSION)-static-mtdll-debug.lib liblept$(LIBLEPT_VERSION)-static-mtdll-debug.lib $(NOINHERIT)"
|
||||
OutputFile="$(OutDir)\$(ProjectName)d.exe"
|
||||
AdditionalLibraryDirectories="..\..\..\lib"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
TargetMachine="17"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="LIB_Release|Win32"
|
||||
OutputDirectory="..\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
InheritedPropertySheets="..\include\tesseract_versionnumbers.vsprops"
|
||||
CharacterSet="2"
|
||||
EnableManagedIncrementalBuild="0"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""
|
||||
Outputs=""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
AdditionalIncludeDirectories="..\..\api;..\..\ccmain;..\..\ccutil;..\..\ccstruct;..\..\classify;..\..\cube;..\..\cutil;..\..\dict;..\..\image;..\..\neural_networks\runtime;..\..\textord;..\..\viewer;..\..\wordrec;.;..\..\..\include;..\..\..\include\leptonica;..\port"
|
||||
PreprocessorDefinitions="WIN32;_WINDOWS;NDEBUG;USE_STD_NAMESPACE"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="0"
|
||||
CompileAs="0"
|
||||
DisableSpecificWarnings="4244;4305;4018;4267;4996;4800;4005;4355;4099;4566"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="MYVERSION="$(LIBTESS_VERSION_R)""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib user32.lib zlib$(ZLIB_VERSION)-static-mtdll.lib libpng$(LIBPNG_VERSION)-static-mtdll.lib libjpeg$(LIBJPEG_VERSION)-static-mtdll.lib giflib$(GIFLIB_VERSION)-static-mtdll.lib libtiff$(LIBTIFF_VERSION)-static-mtdll.lib liblept$(LIBLEPT_VERSION)-static-mtdll.lib $(NOINHERIT)"
|
||||
OutputFile="$(OutDir)\$(ProjectName).exe"
|
||||
AdditionalLibraryDirectories="..\..\..\lib"
|
||||
SubSystem="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="LIB_Release|x64"
|
||||
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
InheritedPropertySheets="..\include\tesseract_versionnumbers.vsprops"
|
||||
CharacterSet="2"
|
||||
EnableManagedIncrementalBuild="0"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""
|
||||
Outputs=""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TargetEnvironment="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
AdditionalIncludeDirectories="..\..\api;..\..\ccmain;..\..\ccutil;..\..\ccstruct;..\..\classify;..\..\cube;..\..\cutil;..\..\dict;..\..\image;..\..\neural_networks\runtime;..\..\textord;..\..\viewer;..\..\wordrec;.;..\..\..\include;..\..\..\include\leptonica;..\port"
|
||||
PreprocessorDefinitions="WIN32;_WINDOWS;NDEBUG;USE_STD_NAMESPACE"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="0"
|
||||
CompileAs="0"
|
||||
DisableSpecificWarnings="4244;4305;4018;4267;4996;4800;4005;4355;4099;4566"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="MYVERSION="$(LIBTESS_VERSION_R)""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib user32.lib zlib$(ZLIB_VERSION)-static-mtdll.lib libpng$(LIBPNG_VERSION)-static-mtdll.lib libjpeg$(LIBJPEG_VERSION)-static-mtdll.lib giflib$(GIFLIB_VERSION)-static-mtdll.lib libtiff$(LIBTIFF_VERSION)-static-mtdll.lib liblept$(LIBLEPT_VERSION)-static-mtdll.lib $(NOINHERIT)"
|
||||
OutputFile="$(OutDir)\$(ProjectName).exe"
|
||||
AdditionalLibraryDirectories="..\..\..\lib"
|
||||
SubSystem="1"
|
||||
TargetMachine="17"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="DLL_Release|Win32"
|
||||
OutputDirectory="..\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\include\tesseract_versionnumbers.vsprops"
|
||||
CharacterSet="2"
|
||||
EnableManagedIncrementalBuild="0"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""
|
||||
Outputs=""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
AdditionalIncludeDirectories="..\..\api;..\..\ccmain;..\..\ccutil;..\..\ccstruct;..\..\classify;..\..\cube;..\..\cutil;..\..\dict;..\..\image;..\..\neural_networks\runtime;..\..\textord;..\..\viewer;..\..\wordrec;.;..\..\..\include;..\..\..\include\leptonica;..\port"
|
||||
PreprocessorDefinitions="WIN32;_WINDOWS;NDEBUG;USE_STD_NAMESPACE;TESS_IMPORTS;LIBLEPT_IMPORTS"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="0"
|
||||
CompileAs="0"
|
||||
DisableSpecificWarnings="4244;4305;4018;4267;4996;4800;4005;4355;4099;4566"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="MYVERSION="$(LIBTESS_VERSION_R)""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib user32.lib liblept$(LIBLEPT_VERSION).lib $(NOINHERIT)"
|
||||
OutputFile="SyncfusionTesseract.dll"
|
||||
Version="$(LIBTESS_NUMBER)"
|
||||
AdditionalLibraryDirectories="..\..\..\lib"
|
||||
SubSystem="1"
|
||||
LinkTimeCodeGeneration="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="DLL_Release|x64"
|
||||
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
InheritedPropertySheets="..\include\tesseract_versionnumbers.vsprops"
|
||||
CharacterSet="2"
|
||||
EnableManagedIncrementalBuild="0"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""
|
||||
Outputs=""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TargetEnvironment="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
AdditionalIncludeDirectories="..\..\api;..\..\ccmain;..\..\ccutil;..\..\ccstruct;..\..\classify;..\..\cube;..\..\cutil;..\..\dict;..\..\image;..\..\neural_networks\runtime;..\..\textord;..\..\viewer;..\..\wordrec;.;..\..\..\include;..\..\..\include\leptonica;..\port"
|
||||
PreprocessorDefinitions="WIN32;_WINDOWS;NDEBUG;USE_STD_NAMESPACE;TESS_IMPORTS;LIBLEPT_IMPORTS"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="0"
|
||||
CompileAs="0"
|
||||
DisableSpecificWarnings="4244;4305;4018;4267;4996;4800;4005;4355;4099;4566"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="MYVERSION="$(LIBTESS_VERSION_R)""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib user32.lib liblept$(LIBLEPT_VERSION).lib $(NOINHERIT)"
|
||||
OutputFile="$(OutDir)\$(ProjectName)-dll.exe"
|
||||
Version="$(LIBTESS_NUMBER)"
|
||||
AdditionalLibraryDirectories="..\..\..\lib"
|
||||
SubSystem="1"
|
||||
LinkTimeCodeGeneration="0"
|
||||
TargetMachine="17"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="DLL_Debug|Win32"
|
||||
OutputDirectory="..\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\include\tesseract_versionnumbers.vsprops"
|
||||
CharacterSet="2"
|
||||
ManagedExtensions="4"
|
||||
EnableManagedIncrementalBuild="0"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""
|
||||
Outputs=""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\api;..\..\ccmain;..\..\ccutil;..\..\ccstruct;..\..\classify;..\..\cube;..\..\cutil;..\..\dict;..\..\image;..\..\neural_networks\runtime;..\..\textord;..\..\viewer;..\..\wordrec;.;..\..\..\include;..\..\..\include\leptonica;..\port"
|
||||
PreprocessorDefinitions="WIN32;_WINDOWS;_DEBUG;USE_STD_NAMESPACE;TESS_IMPORTS;LIBLEPT_IMPORTS"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="0"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="2"
|
||||
ShowIncludes="false"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="MYVERSION="$(LIBTESS_VERSION_R)""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib user32.lib libleptd.lib"
|
||||
OutputFile=".\bin.dbg\SyncfusionTesseract.dll"
|
||||
Version=""
|
||||
LinkIncremental="2"
|
||||
AdditionalLibraryDirectories=""
|
||||
GenerateDebugInformation="true"
|
||||
AssemblyDebug="1"
|
||||
ProgramDatabaseFile=".\bin.dbg/tesseract.pdb"
|
||||
SubSystem="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
KeyFile="../../../../../../../Common/Keys/sf.snk"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="DLL_Debug|x64"
|
||||
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\include\tesseract_versionnumbers.vsprops"
|
||||
CharacterSet="2"
|
||||
ManagedExtensions="4"
|
||||
EnableManagedIncrementalBuild="0"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""
|
||||
Outputs=""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TargetEnvironment="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\api;..\..\ccmain;..\..\ccutil;..\..\ccstruct;..\..\classify;..\..\cube;..\..\cutil;..\..\dict;..\..\image;..\..\neural_networks\runtime;..\..\textord;..\..\viewer;..\..\wordrec;.;..\..\..\include;..\..\..\include\leptonica;..\port"
|
||||
PreprocessorDefinitions="WIN32;_WINDOWS;_DEBUG;USE_STD_NAMESPACE;TESS_IMPORTS;LIBLEPT_IMPORTS"
|
||||
MinimalRebuild="false"
|
||||
BasicRuntimeChecks="0"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
Detect64BitPortabilityProblems="false"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="2"
|
||||
ShowIncludes="false"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="MYVERSION="$(LIBTESS_VERSION_R)""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib user32.lib libleptd.lib"
|
||||
OutputFile=".\bin.dbg\Tesseract3.dll"
|
||||
Version=""
|
||||
LinkIncremental="2"
|
||||
AdditionalLibraryDirectories=""
|
||||
GenerateDebugInformation="true"
|
||||
AssemblyDebug="1"
|
||||
ProgramDatabaseFile=".\bin.dbg/tesseract.pdb"
|
||||
SubSystem="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="17"
|
||||
KeyFile="C:\test.snk"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
<AssemblyReference
|
||||
RelativePath="System.dll"
|
||||
AssemblyName="System, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"
|
||||
MinFrameworkVersion="131072"
|
||||
/>
|
||||
<AssemblyReference
|
||||
RelativePath="System.Drawing.dll"
|
||||
AssemblyName="System.Drawing, Version=2.0.0.0, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
|
||||
MinFrameworkVersion="131072"
|
||||
/>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\api\tesseractmain.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\resource.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\api\tesseractmain.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\tesseract.rc"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
|
@ -1,73 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: lang_mod_edge.h
|
||||
* Description: Declaration of the Language Model Edge Base Class
|
||||
* Author: Ahmad Abdulkader
|
||||
* Created: 2007
|
||||
*
|
||||
* (C) Copyright 2008, 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.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
// The LangModEdge abstracts an Edge in the language model trie
|
||||
// This is an abstract class that any Language Model Edge should inherit from
|
||||
// It provides methods for:
|
||||
// 1- Returns the class ID corresponding to the edge
|
||||
// 2- If the edge is a valid EndOfWord (EOW)
|
||||
// 3- If the edge is coming from a OutOfDictionary (OOF) state machine
|
||||
// 4- If the edge is a Terminal (has no children)
|
||||
// 5- A Hash of the edge that will be used to retrieve the edge
|
||||
// quickly from the BeamSearch lattice
|
||||
// 6- If two edges are identcial
|
||||
// 7- Returns a verbal description of the edge (use by debuggers)
|
||||
// 8- the language model cost of the edge (if any)
|
||||
// 9- The string corresponding to this edge
|
||||
// 10- Getting and setting the "Root" status of the edge
|
||||
|
||||
#ifndef LANG_MOD_EDGE_H
|
||||
#define LANG_MOD_EDGE_H
|
||||
|
||||
#include "cube_tuning_params.h"
|
||||
#include "char_set.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
class LangModEdge {
|
||||
public:
|
||||
LangModEdge() {}
|
||||
virtual ~LangModEdge() {}
|
||||
|
||||
// The string corresponding to this edge
|
||||
virtual const char_32 * EdgeString() const = 0;
|
||||
// Returns the class ID corresponding to the edge
|
||||
virtual int ClassID() const = 0;
|
||||
// If the edge is the root edge
|
||||
virtual bool IsRoot() const = 0;
|
||||
// Set the Root flag
|
||||
virtual void SetRoot(bool flag) = 0;
|
||||
// If the edge is a valid EndOfWord (EOW)
|
||||
virtual bool IsEOW() const = 0;
|
||||
// is the edge is coming from a OutOfDictionary (OOF) state machine
|
||||
virtual bool IsOOD() const = 0;
|
||||
// Is the edge is a Terminal (has no children)
|
||||
virtual bool IsTerminal() const = 0;
|
||||
// Returns A hash of the edge that will be used to retrieve the edge
|
||||
virtual unsigned int Hash() const = 0;
|
||||
// Are the two edges identcial?
|
||||
virtual bool IsIdentical(LangModEdge *edge) const = 0;
|
||||
// a verbal description of the edge (use by debuggers)
|
||||
virtual char *Description() const = 0;
|
||||
// the language model cost of the edge (if any)
|
||||
virtual int PathCost() const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LANG_MOD_EDGE_H
|
|
@ -1,51 +0,0 @@
|
|||
/*====================================================================*
|
||||
- Copyright (C) 2001 Leptonica. All rights reserved.
|
||||
- This software is distributed in the hope that it will be
|
||||
- useful, but with NO WARRANTY OF ANY KIND.
|
||||
- No author or distributor accepts responsibility to anyone for the
|
||||
- consequences of using this software, or for whether it serves any
|
||||
- particular purpose or works at all, unless he or she says so in
|
||||
- writing. Everyone is granted permission to copy, modify and
|
||||
- redistribute this source code, for commercial or non-commercial
|
||||
- purposes, with the following restrictions: (1) the origin of this
|
||||
- source code must not be misrepresented; (2) modified versions must
|
||||
- be plainly marked as such; and (3) this notice may not be removed
|
||||
- or altered from any source or modified source distribution.
|
||||
*====================================================================*/
|
||||
|
||||
#ifndef LEPTONICA_BMF_H
|
||||
#define LEPTONICA_BMF_H
|
||||
|
||||
/*
|
||||
* bmf.h
|
||||
*
|
||||
* Simple data structure to hold bitmap fonts and related data
|
||||
*/
|
||||
|
||||
/* Constants for deciding when text block is divided into paragraphs */
|
||||
enum {
|
||||
SPLIT_ON_LEADING_WHITE = 1, /* tab or space at beginning of line */
|
||||
SPLIT_ON_BLANK_LINE = 2, /* newline with optional white space */
|
||||
SPLIT_ON_BOTH = 3 /* leading white space or newline */
|
||||
};
|
||||
|
||||
|
||||
struct L_Bmf
|
||||
{
|
||||
struct Pixa *pixa; /* pixa of bitmaps for 93 characters */
|
||||
l_int32 size; /* font size (in points at 300 ppi) */
|
||||
char *directory; /* directory containing font bitmaps */
|
||||
l_int32 baseline1; /* baseline offset for ascii 33 - 57 */
|
||||
l_int32 baseline2; /* baseline offset for ascii 58 - 91 */
|
||||
l_int32 baseline3; /* baseline offset for ascii 93 - 126 */
|
||||
l_int32 lineheight; /* max height of line of chars */
|
||||
l_int32 kernwidth; /* pixel dist between char bitmaps */
|
||||
l_int32 spacewidth; /* pixel dist between word bitmaps */
|
||||
l_int32 vertlinesep; /* extra vertical space between text lines */
|
||||
l_int32 *fonttab; /* table mapping ascii --> font index */
|
||||
l_int32 *baselinetab; /* table mapping ascii --> baseline offset */
|
||||
l_int32 *widthtab; /* table mapping ascii --> char width */
|
||||
};
|
||||
typedef struct L_Bmf L_BMF;
|
||||
|
||||
#endif /* LEPTONICA_BMF_H */
|
|
@ -1,370 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: charclassifier.cpp
|
||||
* Description: Implementation of Convolutional-NeuralNet Character Classifier
|
||||
* Author: Ahmad Abdulkader
|
||||
* Created: 2007
|
||||
*
|
||||
* (C) Copyright 2008, 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 <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <wctype.h>
|
||||
|
||||
#include "char_set.h"
|
||||
#include "classifier_base.h"
|
||||
#include "const.h"
|
||||
#include "conv_net_classifier.h"
|
||||
#include "cube_utils.h"
|
||||
#include "feature_base.h"
|
||||
#include "feature_bmp.h"
|
||||
#include "tess_lang_model.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
ConvNetCharClassifier::ConvNetCharClassifier(CharSet *char_set,
|
||||
TuningParams *params,
|
||||
FeatureBase *feat_extract)
|
||||
: CharClassifier(char_set, params, feat_extract) {
|
||||
char_net_ = NULL;
|
||||
net_input_ = NULL;
|
||||
net_output_ = NULL;
|
||||
}
|
||||
|
||||
ConvNetCharClassifier::~ConvNetCharClassifier() {
|
||||
if (char_net_ != NULL) {
|
||||
delete char_net_;
|
||||
char_net_ = NULL;
|
||||
}
|
||||
|
||||
if (net_input_ != NULL) {
|
||||
delete []net_input_;
|
||||
net_input_ = NULL;
|
||||
}
|
||||
|
||||
if (net_output_ != NULL) {
|
||||
delete []net_output_;
|
||||
net_output_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// The main training function. Given a sample and a class ID the classifier
|
||||
// updates its parameters according to its learning algorithm. This function
|
||||
// is currently not implemented. TODO(ahmadab): implement end-2-end training
|
||||
bool ConvNetCharClassifier::Train(CharSamp *char_samp, int ClassID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A secondary function needed for training. Allows the trainer to set the
|
||||
// value of any train-time paramter. This function is currently not
|
||||
// implemented. TODO(ahmadab): implement end-2-end training
|
||||
bool ConvNetCharClassifier::SetLearnParam(char *var_name, float val) {
|
||||
// TODO(ahmadab): implementation of parameter initializing.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Folds the output of the NeuralNet using the loaded folding sets
|
||||
void ConvNetCharClassifier::Fold() {
|
||||
// in case insensitive mode
|
||||
if (case_sensitive_ == false) {
|
||||
int class_cnt = char_set_->ClassCount();
|
||||
// fold case
|
||||
for (int class_id = 0; class_id < class_cnt; class_id++) {
|
||||
// get class string
|
||||
const char_32 *str32 = char_set_->ClassString(class_id);
|
||||
// get the upper case form of the string
|
||||
string_32 upper_form32 = str32;
|
||||
for (int ch = 0; ch < upper_form32.length(); ch++) {
|
||||
if (iswalpha(static_cast<int>(upper_form32[ch])) != 0) {
|
||||
upper_form32[ch] = towupper(upper_form32[ch]);
|
||||
}
|
||||
}
|
||||
|
||||
// find out the upperform class-id if any
|
||||
int upper_class_id =
|
||||
char_set_->ClassID(reinterpret_cast<const char_32 *>(
|
||||
upper_form32.c_str()));
|
||||
if (upper_class_id != -1 && class_id != upper_class_id) {
|
||||
float max_out = MAX(net_output_[class_id], net_output_[upper_class_id]);
|
||||
net_output_[class_id] = max_out;
|
||||
net_output_[upper_class_id] = max_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The folding sets specify how groups of classes should be folded
|
||||
// Folding involved assigning a min-activation to all the members
|
||||
// of the folding set. The min-activation is a fraction of the max-activation
|
||||
// of the members of the folding set
|
||||
for (int fold_set = 0; fold_set < fold_set_cnt_; fold_set++) {
|
||||
if (fold_set_len_[fold_set] == 0)
|
||||
continue;
|
||||
float max_prob = net_output_[fold_sets_[fold_set][0]];
|
||||
for (int ch = 1; ch < fold_set_len_[fold_set]; ch++) {
|
||||
if (net_output_[fold_sets_[fold_set][ch]] > max_prob) {
|
||||
max_prob = net_output_[fold_sets_[fold_set][ch]];
|
||||
}
|
||||
}
|
||||
for (int ch = 0; ch < fold_set_len_[fold_set]; ch++) {
|
||||
net_output_[fold_sets_[fold_set][ch]] = MAX(max_prob * kFoldingRatio,
|
||||
net_output_[fold_sets_[fold_set][ch]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the features of specified charsamp and feedforward the
|
||||
// specified nets
|
||||
bool ConvNetCharClassifier::RunNets(CharSamp *char_samp) {
|
||||
if (char_net_ == NULL) {
|
||||
fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::RunNets): "
|
||||
"NeuralNet is NULL\n");
|
||||
return false;
|
||||
}
|
||||
int feat_cnt = char_net_->in_cnt();
|
||||
int class_cnt = char_set_->ClassCount();
|
||||
|
||||
// allocate i/p and o/p buffers if needed
|
||||
if (net_input_ == NULL) {
|
||||
net_input_ = new float[feat_cnt];
|
||||
if (net_input_ == NULL) {
|
||||
fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::RunNets): "
|
||||
"unable to allocate memory for input nodes\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
net_output_ = new float[class_cnt];
|
||||
if (net_output_ == NULL) {
|
||||
fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::RunNets): "
|
||||
"unable to allocate memory for output nodes\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// compute input features
|
||||
if (feat_extract_->ComputeFeatures(char_samp, net_input_) == false) {
|
||||
fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::RunNets): "
|
||||
"unable to compute features\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (char_net_ != NULL) {
|
||||
if (char_net_->FeedForward(net_input_, net_output_) == false) {
|
||||
fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::RunNets): "
|
||||
"unable to run feed-forward\n");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
Fold();
|
||||
return true;
|
||||
}
|
||||
|
||||
// return the cost of being a char
|
||||
int ConvNetCharClassifier::CharCost(CharSamp *char_samp) {
|
||||
if (RunNets(char_samp) == false) {
|
||||
return 0;
|
||||
}
|
||||
return CubeUtils::Prob2Cost(1.0f - net_output_[0]);
|
||||
}
|
||||
|
||||
// classifies a charsamp and returns an alternate list
|
||||
// of chars sorted by char costs
|
||||
CharAltList *ConvNetCharClassifier::Classify(CharSamp *char_samp) {
|
||||
// run the needed nets
|
||||
if (RunNets(char_samp) == false) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int class_cnt = char_set_->ClassCount();
|
||||
|
||||
// create an altlist
|
||||
CharAltList *alt_list = new CharAltList(char_set_, class_cnt);
|
||||
if (alt_list == NULL) {
|
||||
fprintf(stderr, "Cube WARNING (ConvNetCharClassifier::Classify): "
|
||||
"returning emtpy CharAltList\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int out = 1; out < class_cnt; out++) {
|
||||
int cost = CubeUtils::Prob2Cost(net_output_[out]);
|
||||
alt_list->Insert(out, cost);
|
||||
}
|
||||
|
||||
return alt_list;
|
||||
}
|
||||
|
||||
// Set an external net (for training purposes)
|
||||
void ConvNetCharClassifier::SetNet(tesseract::NeuralNet *char_net) {
|
||||
if (char_net_ != NULL) {
|
||||
delete char_net_;
|
||||
char_net_ = NULL;
|
||||
}
|
||||
char_net_ = char_net;
|
||||
}
|
||||
|
||||
// This function will return true if the file does not exist.
|
||||
// But will fail if the it did not pass the sanity checks
|
||||
bool ConvNetCharClassifier::LoadFoldingSets(const string &data_file_path,
|
||||
const string &lang,
|
||||
LangModel *lang_mod) {
|
||||
fold_set_cnt_ = 0;
|
||||
string fold_file_name;
|
||||
fold_file_name = data_file_path + lang;
|
||||
fold_file_name += ".cube.fold";
|
||||
|
||||
// folding sets are optional
|
||||
FILE *fp = fopen(fold_file_name.c_str(), "rb");
|
||||
if (fp == NULL) {
|
||||
return true;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
string fold_sets_str;
|
||||
if (!CubeUtils::ReadFileToString(fold_file_name.c_str(),
|
||||
&fold_sets_str)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// split into lines
|
||||
vector<string> str_vec;
|
||||
CubeUtils::SplitStringUsing(fold_sets_str, "\r\n", &str_vec);
|
||||
fold_set_cnt_ = str_vec.size();
|
||||
|
||||
fold_sets_ = new int *[fold_set_cnt_];
|
||||
if (fold_sets_ == NULL) {
|
||||
return false;
|
||||
}
|
||||
fold_set_len_ = new int[fold_set_cnt_];
|
||||
if (fold_set_len_ == NULL) {
|
||||
fold_set_cnt_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int fold_set = 0; fold_set < fold_set_cnt_; fold_set++) {
|
||||
reinterpret_cast<TessLangModel *>(lang_mod)->RemoveInvalidCharacters(
|
||||
&str_vec[fold_set]);
|
||||
|
||||
// if all or all but one character are invalid, invalidate this set
|
||||
if (str_vec[fold_set].length() <= 1) {
|
||||
fprintf(stderr, "Cube WARNING (ConvNetCharClassifier::LoadFoldingSets): "
|
||||
"invalidating folding set %d\n", fold_set);
|
||||
fold_set_len_[fold_set] = 0;
|
||||
fold_sets_[fold_set] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
string_32 str32;
|
||||
CubeUtils::UTF8ToUTF32(str_vec[fold_set].c_str(), &str32);
|
||||
fold_set_len_[fold_set] = str32.length();
|
||||
fold_sets_[fold_set] = new int[fold_set_len_[fold_set]];
|
||||
if (fold_sets_[fold_set] == NULL) {
|
||||
fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::LoadFoldingSets): "
|
||||
"could not allocate folding set\n");
|
||||
fold_set_cnt_ = fold_set;
|
||||
return false;
|
||||
}
|
||||
for (int ch = 0; ch < fold_set_len_[fold_set]; ch++) {
|
||||
fold_sets_[fold_set][ch] = char_set_->ClassID(str32[ch]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Init the classifier provided a data-path and a language string
|
||||
bool ConvNetCharClassifier::Init(const string &data_file_path,
|
||||
const string &lang,
|
||||
LangModel *lang_mod) {
|
||||
if (init_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// load the nets if any. This function will return true if the net file
|
||||
// does not exist. But will fail if the net did not pass the sanity checks
|
||||
if (!LoadNets(data_file_path, lang)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// load the folding sets if any. This function will return true if the
|
||||
// file does not exist. But will fail if the it did not pass the sanity checks
|
||||
if (!LoadFoldingSets(data_file_path, lang, lang_mod)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
init_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load the classifier's Neural Nets
|
||||
// This function will return true if the net file does not exist.
|
||||
// But will fail if the net did not pass the sanity checks
|
||||
bool ConvNetCharClassifier::LoadNets(const string &data_file_path,
|
||||
const string &lang) {
|
||||
string char_net_file;
|
||||
|
||||
// add the lang identifier
|
||||
char_net_file = data_file_path + lang;
|
||||
char_net_file += ".cube.nn";
|
||||
|
||||
// neural network is optional
|
||||
FILE *fp = fopen(char_net_file.c_str(), "rb");
|
||||
if (fp == NULL) {
|
||||
return true;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
// load main net
|
||||
char_net_ = tesseract::NeuralNet::FromFile(char_net_file.c_str());
|
||||
if (char_net_ == NULL) {
|
||||
fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::LoadNets): "
|
||||
"could not load %s\n", char_net_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate net
|
||||
if (char_net_->in_cnt()!= feat_extract_->FeatureCnt()) {
|
||||
fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::LoadNets): "
|
||||
"could not validate net %s\n", char_net_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// alloc net i/o buffers
|
||||
int feat_cnt = char_net_->in_cnt();
|
||||
int class_cnt = char_set_->ClassCount();
|
||||
|
||||
if (char_net_->out_cnt() != class_cnt) {
|
||||
fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::LoadNets): "
|
||||
"output count (%d) and class count (%d) are not equal\n",
|
||||
char_net_->out_cnt(), class_cnt);
|
||||
return false;
|
||||
}
|
||||
|
||||
// allocate i/p and o/p buffers if needed
|
||||
if (net_input_ == NULL) {
|
||||
net_input_ = new float[feat_cnt];
|
||||
if (net_input_ == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
net_output_ = new float[class_cnt];
|
||||
if (net_output_ == NULL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // tesseract
|
|
@ -1,41 +0,0 @@
|
|||
/* -*-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 "host.h"
|
||||
#include "blobs.h"
|
||||
#include "params.h"
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
T y p e s
|
||||
----------------------------------------------------------------------*/
|
||||
#define BASELINE_OFFSET 64
|
||||
#define BASELINE_SCALE 128
|
||||
|
||||
#endif
|
|
@ -1,438 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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 "helpers.h"
|
||||
#include "tprintf.h"
|
||||
#include "strngs.h"
|
||||
#include "genericvector.h"
|
||||
|
||||
#include <assert.h>
|
||||
// Size of buffer needed to host the decimal representation of the maximum
|
||||
// possible length of an int (in 64 bits, being -<20 digits>.
|
||||
const int kMaxIntSize = 22;
|
||||
|
||||
/**********************************************************************
|
||||
* STRING_HEADER provides metadata about the allocated buffer,
|
||||
* including total capacity and how much used (strlen with '\0').
|
||||
*
|
||||
* The implementation hides this header at the start of the data
|
||||
* buffer and appends the string on the end to keep sizeof(STRING)
|
||||
* unchanged from earlier versions so serialization is not affected.
|
||||
*
|
||||
* The collection of MACROS provide different implementations depending
|
||||
* on whether the string keeps track of its strlen or not so that this
|
||||
* feature can be added in later when consumers dont modifify the string
|
||||
**********************************************************************/
|
||||
|
||||
// Smallest string to allocate by default
|
||||
const int kMinCapacity = 16;
|
||||
|
||||
char* STRING::AllocData(int used, int capacity) {
|
||||
data_ = (STRING_HEADER *)alloc_string(capacity + sizeof(STRING_HEADER));
|
||||
|
||||
// header is the metadata for this memory block
|
||||
STRING_HEADER* header = GetHeader();
|
||||
header->capacity_ = capacity;
|
||||
header->used_ = used;
|
||||
return GetCStr();
|
||||
}
|
||||
|
||||
void STRING::DiscardData() {
|
||||
free_string((char *)data_);
|
||||
}
|
||||
|
||||
// This is a private method; ensure FixHeader is called (or used_ is well defined)
|
||||
// beforehand
|
||||
char* STRING::ensure_cstr(inT32 min_capacity) {
|
||||
STRING_HEADER* orig_header = GetHeader();
|
||||
if (min_capacity <= orig_header->capacity_)
|
||||
return ((char *)this->data_) + sizeof(STRING_HEADER);
|
||||
|
||||
// if we are going to grow bigger, than double our existing
|
||||
// size, but if that still is not big enough then keep the
|
||||
// requested capacity
|
||||
if (min_capacity < 2 * orig_header->capacity_)
|
||||
min_capacity = 2 * orig_header->capacity_;
|
||||
|
||||
int alloc = sizeof(STRING_HEADER) + min_capacity;
|
||||
STRING_HEADER* new_header = (STRING_HEADER*)(alloc_string(alloc));
|
||||
|
||||
memcpy(&new_header[1], GetCStr(), orig_header->used_);
|
||||
new_header->capacity_ = min_capacity;
|
||||
new_header->used_ = orig_header->used_;
|
||||
|
||||
// free old memory, then rebind to new memory
|
||||
DiscardData();
|
||||
data_ = new_header;
|
||||
|
||||
assert(InvariantOk());
|
||||
return ((char *)data_) + sizeof(STRING_HEADER);
|
||||
}
|
||||
|
||||
// This is const, but is modifying a mutable field
|
||||
// this way it can be used on const or non-const instances.
|
||||
void STRING::FixHeader() const {
|
||||
const STRING_HEADER* header = GetHeader();
|
||||
if (header->used_ < 0)
|
||||
header->used_ = strlen(GetCStr()) + 1;
|
||||
}
|
||||
|
||||
|
||||
STRING::STRING() {
|
||||
// 0 indicates old NULL -- it doesnt even have '\0'
|
||||
AllocData(0, kMinCapacity);
|
||||
}
|
||||
|
||||
STRING::STRING(const STRING& str) {
|
||||
str.FixHeader();
|
||||
const STRING_HEADER* str_header = str.GetHeader();
|
||||
int str_used = str_header->used_;
|
||||
char *this_cstr = AllocData(str_used, str_used);
|
||||
memcpy(this_cstr, str.GetCStr(), str_used);
|
||||
assert(InvariantOk());
|
||||
}
|
||||
|
||||
STRING::STRING(const char* cstr) {
|
||||
if (cstr == NULL) {
|
||||
AllocData(0, 0);
|
||||
} else {
|
||||
int len = strlen(cstr) + 1;
|
||||
char* this_cstr = AllocData(len, len);
|
||||
memcpy(this_cstr, cstr, len);
|
||||
}
|
||||
assert(InvariantOk());
|
||||
}
|
||||
|
||||
STRING::~STRING() {
|
||||
DiscardData();
|
||||
}
|
||||
|
||||
// Writes to the given file. Returns false in case of error.
|
||||
bool STRING::Serialize(FILE* fp) const {
|
||||
inT32 len = length();
|
||||
if (fwrite(&len, sizeof(len), 1, fp) != 1) return false;
|
||||
if (fwrite(GetCStr(), 1, len, fp) != len) return false;
|
||||
return true;
|
||||
}
|
||||
// Reads from the given file. Returns false in case of error.
|
||||
// If swap is true, assumes a big/little-endian swap is needed.
|
||||
bool STRING::DeSerialize(bool swap, FILE* fp) {
|
||||
inT32 len;
|
||||
if (fread(&len, sizeof(len), 1, fp) != 1) return false;
|
||||
if (swap)
|
||||
ReverseN(&len, sizeof(len));
|
||||
truncate_at(len);
|
||||
if (fread(GetCStr(), 1, len, fp) != len) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL8 STRING::contains(const char c) const {
|
||||
return (c != '\0') && (strchr (GetCStr(), c) != NULL);
|
||||
}
|
||||
|
||||
inT32 STRING::length() const {
|
||||
FixHeader();
|
||||
return GetHeader()->used_ - 1;
|
||||
}
|
||||
|
||||
const char* STRING::string() const {
|
||||
const STRING_HEADER* header = GetHeader();
|
||||
if (header->used_ == 0)
|
||||
return NULL;
|
||||
|
||||
// mark header length unreliable because tesseract might
|
||||
// cast away the const and mutate the string directly.
|
||||
header->used_ = -1;
|
||||
return GetCStr();
|
||||
}
|
||||
|
||||
/******
|
||||
* The STRING_IS_PROTECTED interface adds additional support to migrate
|
||||
* code that needs to modify the STRING in ways not otherwise supported
|
||||
* without violating encapsulation.
|
||||
*
|
||||
* Also makes the [] operator return a const so it is immutable
|
||||
*/
|
||||
#if STRING_IS_PROTECTED
|
||||
const char& STRING::operator[](inT32 index) const {
|
||||
return GetCStr()[index];
|
||||
}
|
||||
|
||||
void STRING::insert_range(inT32 index, const char* str, int len) {
|
||||
// if index is outside current range, then also grow size of string
|
||||
// to accmodate the requested range.
|
||||
STRING_HEADER* this_header = GetHeader();
|
||||
int used = this_header->used_;
|
||||
if (index > used)
|
||||
used = index;
|
||||
|
||||
char* this_cstr = ensure_cstr(used + len + 1);
|
||||
if (index < used) {
|
||||
// move existing string from index to '\0' inclusive.
|
||||
memmove(this_cstr + index + len,
|
||||
this_cstr + index,
|
||||
this_header->used_ - index);
|
||||
} else if (len > 0) {
|
||||
// We are going to overwrite previous null terminator, so write the new one.
|
||||
this_cstr[this_header->used_ + len - 1] = '\0';
|
||||
|
||||
// If the old header did not have the terminator,
|
||||
// then we need to account for it now that we've added it.
|
||||
// Otherwise it was already accounted for; we just moved it.
|
||||
if (this_header->used_ == 0)
|
||||
++this_header->used_;
|
||||
}
|
||||
|
||||
// Write new string to index.
|
||||
// The string is already terminated from the conditions above.
|
||||
memcpy(this_cstr + index, str, len);
|
||||
this_header->used_ += len;
|
||||
|
||||
assert(InvariantOk());
|
||||
}
|
||||
|
||||
void STRING::erase_range(inT32 index, int len) {
|
||||
char* this_cstr = GetCStr();
|
||||
STRING_HEADER* this_header = GetHeader();
|
||||
|
||||
memcpy(this_cstr+index, this_cstr+index+len,
|
||||
this_header->used_ - index - len);
|
||||
this_header->used_ -= len;
|
||||
assert(InvariantOk());
|
||||
}
|
||||
|
||||
#else
|
||||
void STRING::truncate_at(inT32 index) {
|
||||
char* this_cstr = ensure_cstr(index + 1);
|
||||
this_cstr[index] = '\0';
|
||||
GetHeader()->used_ = index + 1;
|
||||
assert(InvariantOk());
|
||||
}
|
||||
|
||||
char& STRING::operator[](inT32 index) const {
|
||||
// Code is casting away this const and mutating the string,
|
||||
// so mark used_ as -1 to flag it unreliable.
|
||||
GetHeader()->used_ = -1;
|
||||
return ((char *)GetCStr())[index];
|
||||
}
|
||||
#endif
|
||||
|
||||
void STRING::split(const char c, GenericVector<STRING> *splited) {
|
||||
int start_index = 0;
|
||||
for (int i = 0; i < length(); i++) {
|
||||
if ((*this)[i] == c) {
|
||||
if (i != start_index) {
|
||||
(*this)[i] = '\0';
|
||||
STRING tmp = GetCStr() + start_index;
|
||||
splited->push_back(tmp);
|
||||
(*this)[i] = c;
|
||||
}
|
||||
start_index = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (length() != start_index) {
|
||||
STRING tmp = GetCStr() + start_index;
|
||||
splited->push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL8 STRING::operator==(const STRING& str) const {
|
||||
FixHeader();
|
||||
str.FixHeader();
|
||||
const STRING_HEADER* str_header = str.GetHeader();
|
||||
const STRING_HEADER* this_header = GetHeader();
|
||||
int this_used = this_header->used_;
|
||||
int str_used = str_header->used_;
|
||||
|
||||
return (this_used == str_used)
|
||||
&& (memcmp(GetCStr(), str.GetCStr(), this_used) == 0);
|
||||
}
|
||||
|
||||
BOOL8 STRING::operator!=(const STRING& str) const {
|
||||
FixHeader();
|
||||
str.FixHeader();
|
||||
const STRING_HEADER* str_header = str.GetHeader();
|
||||
const STRING_HEADER* this_header = GetHeader();
|
||||
int this_used = this_header->used_;
|
||||
int str_used = str_header->used_;
|
||||
|
||||
return (this_used != str_used)
|
||||
|| (memcmp(GetCStr(), str.GetCStr(), this_used) != 0);
|
||||
}
|
||||
|
||||
BOOL8 STRING::operator!=(const char* cstr) const {
|
||||
FixHeader();
|
||||
const STRING_HEADER* this_header = GetHeader();
|
||||
|
||||
if (cstr == NULL)
|
||||
return this_header->used_ > 1; // either '\0' or NULL
|
||||
else {
|
||||
inT32 length = strlen(cstr) + 1;
|
||||
return (this_header->used_ != length)
|
||||
|| (memcmp(GetCStr(), cstr, length) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
STRING& STRING::operator=(const STRING& str) {
|
||||
str.FixHeader();
|
||||
const STRING_HEADER* str_header = str.GetHeader();
|
||||
int str_used = str_header->used_;
|
||||
|
||||
GetHeader()->used_ = 0; // clear since ensure doesnt need to copy data
|
||||
char* this_cstr = ensure_cstr(str_used);
|
||||
STRING_HEADER* this_header = GetHeader();
|
||||
|
||||
memcpy(this_cstr, str.GetCStr(), str_used);
|
||||
this_header->used_ = str_used;
|
||||
|
||||
assert(InvariantOk());
|
||||
return *this;
|
||||
}
|
||||
|
||||
STRING & STRING::operator+=(const STRING& str) {
|
||||
FixHeader();
|
||||
str.FixHeader();
|
||||
const STRING_HEADER* str_header = str.GetHeader();
|
||||
const char* str_cstr = str.GetCStr();
|
||||
int str_used = str_header->used_;
|
||||
int this_used = GetHeader()->used_;
|
||||
char* this_cstr = ensure_cstr(this_used + str_used);
|
||||
|
||||
STRING_HEADER* this_header = GetHeader(); // after ensure for realloc
|
||||
|
||||
if (this_used > 1) {
|
||||
memcpy(this_cstr + this_used - 1, str_cstr, str_used);
|
||||
this_header->used_ += str_used - 1; // overwrite '\0'
|
||||
} else {
|
||||
memcpy(this_cstr, str_cstr, str_used);
|
||||
this_header->used_ = str_used;
|
||||
}
|
||||
|
||||
assert(InvariantOk());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void STRING::add_str_int(const char* str, int number) {
|
||||
if (str != NULL)
|
||||
*this += str;
|
||||
// Allow space for the maximum possible length of inT64.
|
||||
char num_buffer[kMaxIntSize];
|
||||
snprintf(num_buffer, kMaxIntSize - 1, "%d", number);
|
||||
num_buffer[kMaxIntSize - 1] = '\0';
|
||||
*this += num_buffer;
|
||||
}
|
||||
|
||||
STRING & STRING::operator=(const char* cstr) {
|
||||
STRING_HEADER* this_header = GetHeader();
|
||||
if (cstr) {
|
||||
int len = strlen(cstr) + 1;
|
||||
|
||||
this_header->used_ = 0; // dont bother copying data if need to realloc
|
||||
char* this_cstr = ensure_cstr(len);
|
||||
this_header = GetHeader(); // for realloc
|
||||
memcpy(this_cstr, cstr, len);
|
||||
this_header->used_ = len;
|
||||
}
|
||||
else {
|
||||
// Reallocate to zero capacity buffer, consistent with the corresponding
|
||||
// copy constructor.
|
||||
DiscardData();
|
||||
AllocData(0, 0);
|
||||
}
|
||||
|
||||
assert(InvariantOk());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
STRING STRING::operator+(const STRING& str) const {
|
||||
STRING result(*this);
|
||||
result += str;
|
||||
|
||||
assert(InvariantOk());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
STRING STRING::operator+(const char ch) const {
|
||||
STRING result;
|
||||
FixHeader();
|
||||
const STRING_HEADER* this_header = GetHeader();
|
||||
int this_used = this_header->used_;
|
||||
char* result_cstr = result.ensure_cstr(this_used + 1);
|
||||
STRING_HEADER* result_header = result.GetHeader();
|
||||
int result_used = result_header->used_;
|
||||
|
||||
// copies '\0' but we'll overwrite that
|
||||
memcpy(result_cstr, GetCStr(), this_used);
|
||||
result_cstr[result_used] = ch; // overwrite old '\0'
|
||||
result_cstr[result_used + 1] = '\0'; // append on '\0'
|
||||
++result_header->used_;
|
||||
|
||||
assert(InvariantOk());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
STRING& STRING::operator+=(const char *str) {
|
||||
if (!str || !*str) // empty string has no effect
|
||||
return *this;
|
||||
|
||||
FixHeader();
|
||||
int len = strlen(str) + 1;
|
||||
int this_used = GetHeader()->used_;
|
||||
char* this_cstr = ensure_cstr(this_used + len);
|
||||
STRING_HEADER* this_header = GetHeader(); // after ensure for realloc
|
||||
|
||||
// if we had non-empty string then append overwriting old '\0'
|
||||
// otherwise replace
|
||||
if (this_used > 0) {
|
||||
memcpy(this_cstr + this_used - 1, str, len);
|
||||
this_header->used_ += len - 1;
|
||||
} else {
|
||||
memcpy(this_cstr, str, len);
|
||||
this_header->used_ = len;
|
||||
}
|
||||
|
||||
assert(InvariantOk());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
STRING& STRING::operator+=(const char ch) {
|
||||
if (ch == '\0')
|
||||
return *this;
|
||||
|
||||
FixHeader();
|
||||
int this_used = GetHeader()->used_;
|
||||
char* this_cstr = ensure_cstr(this_used + 1);
|
||||
STRING_HEADER* this_header = GetHeader();
|
||||
|
||||
if (this_used > 0)
|
||||
--this_used; // undo old empty null if there was one
|
||||
|
||||
this_cstr[this_used++] = ch; // append ch to end
|
||||
this_cstr[this_used++] = '\0'; // append '\0' after ch
|
||||
this_header->used_ = this_used;
|
||||
|
||||
assert(InvariantOk());
|
||||
return *this;
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
2012-02-01 - v3.02
|
||||
* Moved ResultIterator/PageIterator to ccmain.
|
||||
* Added Right-to-left/Bidi capability in the output iterators for Hebrew/Arabic.
|
||||
* Added paragraph detection in layout analysis/post OCR.
|
||||
* Fixed inconsistent xheight during training and over-chopping.
|
||||
* Added simultaneous multi-language capability.
|
||||
* Refactored top-level word recognition module.
|
||||
* Added experimental equation detector.
|
||||
* Improved handling of resolution from input images.
|
||||
* Blamer module added for error analysis.
|
||||
* Cleaned up externally used namespace by removing includes from baseapi.h.
|
||||
* Removed dead memory mangagement code.
|
||||
* Tidied up constraints on control parameters.
|
||||
* Added support for ShapeTable in classifier and training.
|
||||
* Refactored class pruner.
|
||||
* Fixed training leaks and randomness.
|
||||
* Major improvements to layout analysis for better image detection, diacritic detection, better textline finding, better tabstop finding.
|
||||
* Improved line detection and removal.
|
||||
* Added fixed pitch chopper for CJK.
|
||||
* Added UNICHARSET to WERD_CHOICE to make mult-language handling easier.
|
||||
* Fixed problems with internally scaled images.
|
||||
* Added page and bbox to string in tr files to identify source of training data better.
|
||||
* Fixes to Hindi Shiroreka splitter.
|
||||
* Added word bigram correction.
|
||||
* Reduced stack memory consumption and eliminated some ugly typedefs.
|
||||
* Added new uniform classifier API.
|
||||
* Added new training error counter.
|
||||
* Fixed endian bug in dawg reader.
|
||||
* Many other fixes, including the way in which the chopper finds chops and messes with the outline while it does so.
|
||||
|
||||
2010-11-29 - V3.01
|
||||
* Removed old/dead serialise/deserialze methods on *LISTIZED classes.
|
||||
* Total rewrite of DENORM to better encapsulate operation and make
|
||||
for potential to extract features from images.
|
||||
* Thread-safety! Moved all critical globals and statics to members of the appropriate class. Tesseract is now thread-safe (multiple instances can be used in parallel in multiple threads.) with the minor exception that some control parameters are still global and affect all threads.
|
||||
* Added Cube, a new recognizer for Arabic. Cube can also be used in combination with normal Tesseract for other languages with an improvement in accuracy at the cost of (much) lower speed. *There is no training module for Cube yet.*
|
||||
* `OcrEngineMode` in `Init` replaces `AccuracyVSpeed` to control cube.
|
||||
* Greatly improved segmentation search with consequent accuracy and speed improvements, especially for Chinese.
|
||||
* Added `PageIterator` and `ResultIterator` as cleaner ways to get the full results out of Tesseract, that are not currently provided by any of the `TessBaseAPI::Get*` methods. All other methods, such as the `ETEXT_STRUCT` in particular are deprecated and will be deleted in the future.
|
||||
* ApplyBoxes totally rewritten to make training easier. It can now cope with touching/overlapping training characters, and a new boxfile format allows word boxes instead of character boxes, BUT to use that you have to have already boostrapped the language with character boxes. "Cyclic dependency" on traineddata.
|
||||
* Auto orientation and script detection added to page layout analysis.
|
||||
* Deleted *lots* of dead code.
|
||||
* Fixxht module replaced with scalable data-driven module.
|
||||
* Output font characteristics accuracy improved.
|
||||
* Removed the double conversion at each classification.
|
||||
* Upgraded oldest structs to be classes and deprecated PBLOB.
|
||||
* Removed non-deterministic baseline fit.
|
||||
* Added fixed length dawgs for Chinese.
|
||||
* Handling of vertical text improved.
|
||||
* Handling of leader dots improved.
|
||||
* Table detection greatly improved.
|
||||
* Fixed a couple of memory leaks.
|
||||
* Fixed font labels on output text. (Not perfect, but a lot better than before.)
|
||||
* Cleanup and more bug fixes
|
||||
* Special treatments for Hindi.
|
||||
* Support for build in VS2010 with Microsoft Windows SDK for Windows 7 (thanks to Michael Lutz)
|
||||
|
||||
2010-09-21 - V3.00
|
||||
* Preparations for thread safety:
|
||||
* Changed TessBaseAPI methods to be non-static
|
||||
* Created a class hierarchy for the directories to hold instance data,
|
||||
and began moving code into the classes.
|
||||
* Moved thresholding code to a separate class.
|
||||
* Added major new page layout analysis module.
|
||||
* Added HOCR output (issues 221, 263: thanks to amkryukov).
|
||||
* Added Leptonica as main image I/O and handling. Currently optional,
|
||||
but in future releases linking with Leptonica will be mandatory.
|
||||
* Ambiguity table rewritten to allow definite replacements in place
|
||||
of fix_quotes.
|
||||
* Added TessdataManager to combine data files into a single file.
|
||||
* Some dead code deleted.
|
||||
* VC++6 no longer supported. It can't cope with the use of templates.
|
||||
* Many more languages added.
|
||||
* Doxygenation of most of the function header comments.
|
||||
* Added man pages.
|
||||
* Added bash completion script (issue 247: thanks to neskiem)
|
||||
* Fix integer overview in thresholding (issue 366: thanks to Cyanide.Drake)
|
||||
* Add Danish Fraktur support (issues 300, 360: thanks to
|
||||
dsl602230@vip.cybercity.dk)
|
||||
* Fix file pointer leak (issue 359, thanks to yukihiro.nakadaira)
|
||||
* Fix an error using user-words (Issue 345: thanks to max.markin)
|
||||
* Fix a memory leak in tablefind.cpp (Issue 342, thanks to zdravco)
|
||||
* Fix a segfault due to double fclose (Issue 320, thanks to souther)
|
||||
* Fix an automake error (Issue 318, thanks to ichanjz)
|
||||
* Fix a Win32 crash on fileFormatIsTiff() (Issues 304, 316, 317, 330, 347,
|
||||
349, 352: thanks to nguyenq87, max.markin, zdenop)
|
||||
* Fixed a number of errors in newer (stricter) versions of VC++ (Issues
|
||||
301, among others)
|
||||
|
||||
2010-07-19 gettextize <bug-gnu-gettext@gnu.org>
|
||||
|
||||
* m4/gettext.m4: New file, from gettext-0.17.
|
||||
* m4/iconv.m4: New file, from gettext-0.17.
|
||||
* m4/lib-ld.m4: New file, from gettext-0.17.
|
||||
* m4/lib-link.m4: New file, from gettext-0.17.
|
||||
* m4/lib-prefix.m4: New file, from gettext-0.17.
|
||||
* m4/nls.m4: New file, from gettext-0.17.
|
||||
* m4/po.m4: New file, from gettext-0.17.
|
||||
* m4/progtest.m4: New file, from gettext-0.17.
|
||||
* Makefile.am (SUBDIRS): Add po.
|
||||
(EXTRA_DIST): Add config/config.rpath.
|
||||
* configure.ac (AC_CONFIG_FILES): Add po/Makefile.in.
|
||||
|
||||
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.
|
||||
May 15 2007 - V1.04
|
||||
Added dll exports for Windows.
|
||||
Fixed name collisions with stl etc.
|
||||
Made some preliminary changes ready for unicodeization.
|
||||
Several bug fixes discovered during unicodeization.
|
||||
July 02 2007 - V2.00
|
||||
Converted internal character handling to UTF8.
|
||||
Trained with 6 languages.
|
||||
Added unicharset_extractor, wordlist2dawg.
|
||||
Added boxfile creation mode.
|
||||
Added UNLV regression test capability.
|
||||
Fixed problems with copyright and registered symbols.
|
||||
Fixed extern "C" declarations problem.
|
||||
August 27 2007 - V2.01
|
||||
Fixed UTF8 input problems with box file reader.
|
||||
Fixed various infinite loops and crashes in dawg code.
|
||||
Removed include of config_auto.h from host.h.
|
||||
Added automatic wctype encoding to unicharset_extractor.
|
||||
Fixed dawg table too full error.
|
||||
Removed svn files from tarball.
|
||||
Added new functions to tessdll.
|
||||
Increased maximum utf8 string in a classification result to 8.
|
||||
|
||||
January 23 2008 - V2.02
|
||||
Improvements to clustering, training and classifier.
|
||||
Major internationalization improvements for large-character-set
|
||||
languages, eg Kannada.
|
||||
Removed some compiler warnings.
|
||||
Added multipage tiff support for training and running.
|
||||
Updated graphics output to talk to new java-based viewer.
|
||||
Added ability to save n-best lists.
|
||||
Added leptonica support for more file types.
|
||||
Improved Init/End to make them safe.
|
||||
Reduced memory use of dictionaries.
|
||||
Added some new APIs to TessBaseAPI.
|
||||
April 21 2008 - V2.02 (again)
|
||||
Fixed namespace collisions with jpeg library (INT32).
|
||||
Portability fixes for Windows for new code.
|
||||
Updates to autoconf system for new code.
|
||||
April 22 2008 - V2.03
|
||||
Fixed crash introduced in 2.02.
|
||||
Fixed lack of tessembedded.cpp in distribution.
|
||||
Added test for leptonica header files and conditional test for lib.
|
||||
June 30 2009 - V2.04
|
||||
Integrated bug fixes and patches and misc changes for portability.
|
||||
Integrated a patch to remove some of the "access" macros.
|
||||
Removed dependence on lua from the viewer, speeding it up
|
||||
dramatically.
|
||||
Fixed the viewer so it compiles and runs properly!
|
||||
Specifically fixing issues: 1, 63, 67, 71, 76, 81, 82, 106, 111,
|
||||
112, 128, 129, 130, 133, 135, 142, 143, 145, 147, 153, 154, 160,
|
||||
165, 170, 175, 177, 187, 192, 195, 199, 201, 205, 209, 108, 169
|
|
@ -1,50 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: word_altlist.h
|
||||
* Description: Declaration of the Word Alternate List Class
|
||||
* Author: Ahmad Abdulkader
|
||||
* Created: 2008
|
||||
*
|
||||
* (C) Copyright 2008, 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.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
// The WordAltList abstracts a alternate list of words and their corresponding
|
||||
// costs that result from the word recognition process. The class inherits
|
||||
// from the AltList class
|
||||
// It provides methods to add a new word alternate, its corresponding score and
|
||||
// a tag.
|
||||
|
||||
#ifndef WORD_ALT_LIST_H
|
||||
#define WORD_ALT_LIST_H
|
||||
|
||||
#include "altlist.h"
|
||||
|
||||
namespace tesseract {
|
||||
class WordAltList : public AltList {
|
||||
public:
|
||||
explicit WordAltList(int max_alt);
|
||||
~WordAltList();
|
||||
// Sort the list of alternates based on cost
|
||||
void Sort();
|
||||
// insert an alternate word with the specified cost and tag
|
||||
bool Insert(char_32 *char_ptr, int cost, void *tag = NULL);
|
||||
// returns the alternate string at the specified position
|
||||
inline char_32 * Alt(int alt_idx) { return word_alt_[alt_idx]; }
|
||||
// print each entry of the altlist, both UTF8 and unichar ids, and
|
||||
// their costs, to stderr
|
||||
void PrintDebug();
|
||||
private:
|
||||
char_32 **word_alt_;
|
||||
};
|
||||
} // namespace tesseract
|
||||
|
||||
#endif // WORD_ALT_LIST_H
|
|
@ -1,945 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: cube_page_segmenter.cpp
|
||||
* Description: Implementation of the Cube Page Segmenter Class
|
||||
* Author: Ahmad Abdulkader
|
||||
* Created: 2007
|
||||
*
|
||||
* (C) Copyright 2008, 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 "cube_line_segmenter.h"
|
||||
#include "ndminx.h"
|
||||
|
||||
namespace tesseract {
|
||||
// constants that worked for Arabic page segmenter
|
||||
const int CubeLineSegmenter::kLineSepMorphMinHgt = 20;
|
||||
const int CubeLineSegmenter::kHgtBins = 20;
|
||||
const double CubeLineSegmenter::kMaxValidLineRatio = 3.2;
|
||||
const int CubeLineSegmenter::kMaxConnCompHgt = 150;
|
||||
const int CubeLineSegmenter::kMaxConnCompWid = 500;
|
||||
const int CubeLineSegmenter::kMaxHorzAspectRatio = 50;
|
||||
const int CubeLineSegmenter::kMaxVertAspectRatio = 20;
|
||||
const int CubeLineSegmenter::kMinWid = 2;
|
||||
const int CubeLineSegmenter::kMinHgt = 2;
|
||||
const float CubeLineSegmenter::kMinValidLineHgtRatio = 2.5;
|
||||
|
||||
CubeLineSegmenter::CubeLineSegmenter(CubeRecoContext *cntxt, Pix *img) {
|
||||
cntxt_ = cntxt;
|
||||
orig_img_ = img;
|
||||
img_ = NULL;
|
||||
lines_pixa_ = NULL;
|
||||
init_ = false;
|
||||
line_cnt_ = 0;
|
||||
columns_ = NULL;
|
||||
con_comps_ = NULL;
|
||||
est_alef_hgt_ = 0.0;
|
||||
est_dot_hgt_ = 0.0;
|
||||
}
|
||||
|
||||
CubeLineSegmenter::~CubeLineSegmenter() {
|
||||
if (img_ != NULL) {
|
||||
pixDestroy(&img_);
|
||||
img_ = NULL;
|
||||
}
|
||||
|
||||
if (lines_pixa_ != NULL) {
|
||||
pixaDestroy(&lines_pixa_);
|
||||
lines_pixa_ = NULL;
|
||||
}
|
||||
|
||||
if (con_comps_ != NULL) {
|
||||
pixaDestroy(&con_comps_);
|
||||
con_comps_ = NULL;
|
||||
}
|
||||
|
||||
if (columns_ != NULL) {
|
||||
pixaaDestroy(&columns_);
|
||||
columns_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// compute validity ratio for a line
|
||||
double CubeLineSegmenter::ValidityRatio(Pix *line_mask_pix, Box *line_box) {
|
||||
return line_box->h / est_alef_hgt_;
|
||||
}
|
||||
|
||||
// validate line
|
||||
bool CubeLineSegmenter::ValidLine(Pix *line_mask_pix, Box *line_box) {
|
||||
double validity_ratio = ValidityRatio(line_mask_pix, line_box);
|
||||
|
||||
return validity_ratio < kMaxValidLineRatio;
|
||||
}
|
||||
|
||||
// perform a vertical Closing with the specified threshold
|
||||
// returning the resulting conn comps as a pixa
|
||||
Pixa *CubeLineSegmenter::VerticalClosing(Pix *pix,
|
||||
int threshold, Boxa **boxa) {
|
||||
char sequence_str[16];
|
||||
|
||||
// do the morphology
|
||||
sprintf(sequence_str, "c100.%d", threshold);
|
||||
Pix *morphed_pix = pixMorphCompSequence(pix, sequence_str, 0);
|
||||
if (morphed_pix == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get the resulting lines by computing concomps
|
||||
Pixa *pixac;
|
||||
(*boxa) = pixConnComp(morphed_pix, &pixac, 8);
|
||||
|
||||
pixDestroy(&morphed_pix);
|
||||
|
||||
if ((*boxa) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pixac;
|
||||
}
|
||||
|
||||
// do a desperate attempt at cracking lines
|
||||
Pixa *CubeLineSegmenter::CrackLine(Pix *cracked_line_pix,
|
||||
Box *cracked_line_box, int line_cnt) {
|
||||
// create lines pixa array
|
||||
Pixa **lines_pixa = new Pixa*[line_cnt];
|
||||
if (lines_pixa == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(lines_pixa, 0, line_cnt * sizeof(*lines_pixa));
|
||||
|
||||
// compute line conn comps
|
||||
Pixa *line_con_comps_pix;
|
||||
Boxa *line_con_comps = ComputeLineConComps(cracked_line_pix,
|
||||
cracked_line_box, &line_con_comps_pix);
|
||||
|
||||
if (line_con_comps == NULL) {
|
||||
delete []lines_pixa;
|
||||
return false;
|
||||
}
|
||||
|
||||
// assign each conn comp to the a line based on its centroid
|
||||
for (int con = 0; con < line_con_comps->n; con++) {
|
||||
Box *con_box = line_con_comps->box[con];
|
||||
Pix *con_pix = line_con_comps_pix->pix[con];
|
||||
int mid_y = (con_box->y - cracked_line_box->y) + (con_box->h / 2),
|
||||
line_idx = MIN(line_cnt - 1,
|
||||
(mid_y * line_cnt / cracked_line_box->h));
|
||||
|
||||
// create the line if it has not been created?
|
||||
if (lines_pixa[line_idx] == NULL) {
|
||||
lines_pixa[line_idx] = pixaCreate(line_con_comps->n);
|
||||
if (lines_pixa[line_idx] == NULL) {
|
||||
delete []lines_pixa;
|
||||
boxaDestroy(&line_con_comps);
|
||||
pixaDestroy(&line_con_comps_pix);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// add the concomp to the line
|
||||
if (pixaAddPix(lines_pixa[line_idx], con_pix, L_CLONE) != 0 ||
|
||||
pixaAddBox(lines_pixa[line_idx], con_box, L_CLONE)) {
|
||||
delete []lines_pixa;
|
||||
boxaDestroy(&line_con_comps);
|
||||
pixaDestroy(&line_con_comps_pix);
|
||||
}
|
||||
}
|
||||
|
||||
// create the lines pixa
|
||||
Pixa *lines = pixaCreate(line_cnt);
|
||||
bool success = true;
|
||||
|
||||
// create and check the validity of the lines
|
||||
for (int line = 0; line < line_cnt; line++) {
|
||||
Pixa *line_pixa = lines_pixa[line];
|
||||
|
||||
// skip invalid lines
|
||||
if (line_pixa == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// merge the pix, check the validity of the line
|
||||
// and add it to the lines pixa
|
||||
Box *line_box;
|
||||
Pix *line_pix = Pixa2Pix(line_pixa, &line_box);
|
||||
if (line_pix == NULL ||
|
||||
line_box == NULL ||
|
||||
ValidLine(line_pix, line_box) == false ||
|
||||
pixaAddPix(lines, line_pix, L_INSERT) != 0 ||
|
||||
pixaAddBox(lines, line_box, L_INSERT) != 0) {
|
||||
if (line_pix != NULL) {
|
||||
pixDestroy(&line_pix);
|
||||
}
|
||||
|
||||
if (line_box != NULL) {
|
||||
boxDestroy(&line_box);
|
||||
}
|
||||
|
||||
success = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup
|
||||
for (int line = 0; line < line_cnt; line++) {
|
||||
if (lines_pixa[line] != NULL) {
|
||||
pixaDestroy(&lines_pixa[line]);
|
||||
}
|
||||
}
|
||||
|
||||
delete []lines_pixa;
|
||||
boxaDestroy(&line_con_comps);
|
||||
pixaDestroy(&line_con_comps_pix);
|
||||
|
||||
if (success == false) {
|
||||
pixaDestroy(&lines);
|
||||
lines = NULL;
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
// do a desperate attempt at cracking lines
|
||||
Pixa *CubeLineSegmenter::CrackLine(Pix *cracked_line_pix,
|
||||
Box *cracked_line_box) {
|
||||
// estimate max line count
|
||||
int max_line_cnt = static_cast<int>((cracked_line_box->h /
|
||||
est_alef_hgt_) + 0.5);
|
||||
if (max_line_cnt < 2) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int line_cnt = 2; line_cnt < max_line_cnt; line_cnt++) {
|
||||
Pixa *lines = CrackLine(cracked_line_pix, cracked_line_box, line_cnt);
|
||||
if (lines != NULL) {
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// split a line continously until valid or fail
|
||||
Pixa *CubeLineSegmenter::SplitLine(Pix *line_mask_pix, Box *line_box) {
|
||||
// clone the line mask
|
||||
Pix *line_pix = pixClone(line_mask_pix);
|
||||
|
||||
if (line_pix == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// AND with the image to get the actual line
|
||||
pixRasterop(line_pix, 0, 0, line_pix->w, line_pix->h,
|
||||
PIX_SRC & PIX_DST, img_, line_box->x, line_box->y);
|
||||
|
||||
// continue to do rasterop morphology on the line until
|
||||
// it splits to valid lines or we fail
|
||||
int morph_hgt = kLineSepMorphMinHgt - 1,
|
||||
best_threshold = kLineSepMorphMinHgt - 1,
|
||||
max_valid_portion = 0;
|
||||
|
||||
Boxa *boxa;
|
||||
Pixa *pixac;
|
||||
|
||||
do {
|
||||
pixac = VerticalClosing(line_pix, morph_hgt, &boxa);
|
||||
|
||||
// add the box offset to all the lines
|
||||
// and check for the validity of each
|
||||
int line,
|
||||
valid_line_cnt = 0,
|
||||
valid_portion = 0;
|
||||
|
||||
for (line = 0; line < pixac->n; line++) {
|
||||
boxa->box[line]->x += line_box->x;
|
||||
boxa->box[line]->y += line_box->y;
|
||||
|
||||
if (ValidLine(pixac->pix[line], boxa->box[line]) == true) {
|
||||
// count valid lines
|
||||
valid_line_cnt++;
|
||||
|
||||
// and the valid portions
|
||||
valid_portion += boxa->box[line]->h;
|
||||
}
|
||||
}
|
||||
|
||||
// all the lines are valid
|
||||
if (valid_line_cnt == pixac->n) {
|
||||
boxaDestroy(&boxa);
|
||||
pixDestroy(&line_pix);
|
||||
return pixac;
|
||||
}
|
||||
|
||||
// a larger valid portion
|
||||
if (valid_portion > max_valid_portion) {
|
||||
max_valid_portion = valid_portion;
|
||||
best_threshold = morph_hgt;
|
||||
}
|
||||
|
||||
boxaDestroy(&boxa);
|
||||
pixaDestroy(&pixac);
|
||||
|
||||
morph_hgt--;
|
||||
}
|
||||
while (morph_hgt > 0);
|
||||
|
||||
// failed to break into valid lines
|
||||
// attempt to crack the line
|
||||
pixac = CrackLine(line_pix, line_box);
|
||||
if (pixac != NULL) {
|
||||
pixDestroy(&line_pix);
|
||||
return pixac;
|
||||
}
|
||||
|
||||
// try to leverage any of the lines
|
||||
// did the best threshold yield a non zero valid portion
|
||||
if (max_valid_portion > 0) {
|
||||
// use this threshold to break lines
|
||||
pixac = VerticalClosing(line_pix, best_threshold, &boxa);
|
||||
|
||||
// add the box offset to all the lines
|
||||
// and check for the validity of each
|
||||
for (int line = 0; line < pixac->n; line++) {
|
||||
boxa->box[line]->x += line_box->x;
|
||||
boxa->box[line]->y += line_box->y;
|
||||
|
||||
// remove invalid lines from the pixa
|
||||
if (ValidLine(pixac->pix[line], boxa->box[line]) == false) {
|
||||
pixaRemovePix(pixac, line);
|
||||
line--;
|
||||
}
|
||||
}
|
||||
|
||||
boxaDestroy(&boxa);
|
||||
pixDestroy(&line_pix);
|
||||
return pixac;
|
||||
}
|
||||
|
||||
// last resort: attempt to crack the line
|
||||
pixDestroy(&line_pix);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Checks of a line is too small
|
||||
bool CubeLineSegmenter::SmallLine(Box *line_box) {
|
||||
return line_box->h <= (kMinValidLineHgtRatio * est_dot_hgt_);
|
||||
}
|
||||
|
||||
// Compute the connected components in a line
|
||||
Boxa * CubeLineSegmenter::ComputeLineConComps(Pix *line_mask_pix,
|
||||
Box *line_box,
|
||||
Pixa **con_comps_pixa) {
|
||||
// clone the line mask
|
||||
Pix *line_pix = pixClone(line_mask_pix);
|
||||
|
||||
if (line_pix == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// AND with the image to get the actual line
|
||||
pixRasterop(line_pix, 0, 0, line_pix->w, line_pix->h,
|
||||
PIX_SRC & PIX_DST, img_, line_box->x, line_box->y);
|
||||
|
||||
// compute the connected components of the line to be merged
|
||||
Boxa *line_con_comps = pixConnComp(line_pix, con_comps_pixa, 8);
|
||||
|
||||
pixDestroy(&line_pix);
|
||||
|
||||
// offset boxes by the bbox of the line
|
||||
for (int con = 0; con < line_con_comps->n; con++) {
|
||||
line_con_comps->box[con]->x += line_box->x;
|
||||
line_con_comps->box[con]->y += line_box->y;
|
||||
}
|
||||
|
||||
return line_con_comps;
|
||||
}
|
||||
|
||||
// create a union of two arbitrary pix
|
||||
Pix *CubeLineSegmenter::PixUnion(Pix *dest_pix, Box *dest_box,
|
||||
Pix *src_pix, Box *src_box) {
|
||||
// compute dimensions of union rect
|
||||
BOX *union_box = boxBoundingRegion(src_box, dest_box);
|
||||
|
||||
// create the union pix
|
||||
Pix *union_pix = pixCreate(union_box->w, union_box->h, src_pix->d);
|
||||
if (union_pix == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// blt the src and dest pix
|
||||
pixRasterop(union_pix,
|
||||
src_box->x - union_box->x, src_box->y - union_box->y,
|
||||
src_box->w, src_box->h, PIX_SRC | PIX_DST, src_pix, 0, 0);
|
||||
|
||||
pixRasterop(union_pix,
|
||||
dest_box->x - union_box->x, dest_box->y - union_box->y,
|
||||
dest_box->w, dest_box->h, PIX_SRC | PIX_DST, dest_pix, 0, 0);
|
||||
|
||||
// replace the dest_box
|
||||
*dest_box = *union_box;
|
||||
|
||||
boxDestroy(&union_box);
|
||||
|
||||
return union_pix;
|
||||
}
|
||||
|
||||
// create a union of a number of arbitrary pix
|
||||
Pix *CubeLineSegmenter::Pixa2Pix(Pixa *pixa, Box **dest_box,
|
||||
int start_pix, int pix_cnt) {
|
||||
// compute union_box
|
||||
int min_x = INT_MAX,
|
||||
max_x = INT_MIN,
|
||||
min_y = INT_MAX,
|
||||
max_y = INT_MIN;
|
||||
|
||||
for (int pix_idx = start_pix; pix_idx < (start_pix + pix_cnt); pix_idx++) {
|
||||
Box *pix_box = pixa->boxa->box[pix_idx];
|
||||
|
||||
UpdateRange(pix_box->x, pix_box->x + pix_box->w, &min_x, &max_x);
|
||||
UpdateRange(pix_box->y, pix_box->y + pix_box->h, &min_y, &max_y);
|
||||
}
|
||||
|
||||
(*dest_box) = boxCreate(min_x, min_y, max_x - min_x, max_y - min_y);
|
||||
if ((*dest_box) == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// create the union pix
|
||||
Pix *union_pix = pixCreate((*dest_box)->w, (*dest_box)->h, img_->d);
|
||||
if (union_pix == NULL) {
|
||||
boxDestroy(dest_box);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create a pix corresponding to the union of all pixs
|
||||
// blt the src and dest pix
|
||||
for (int pix_idx = start_pix; pix_idx < (start_pix + pix_cnt); pix_idx++) {
|
||||
Box *pix_box = pixa->boxa->box[pix_idx];
|
||||
Pix *con_pix = pixa->pix[pix_idx];
|
||||
|
||||
pixRasterop(union_pix,
|
||||
pix_box->x - (*dest_box)->x, pix_box->y - (*dest_box)->y,
|
||||
pix_box->w, pix_box->h, PIX_SRC | PIX_DST, con_pix, 0, 0);
|
||||
}
|
||||
|
||||
return union_pix;
|
||||
}
|
||||
|
||||
// create a union of a number of arbitrary pix
|
||||
Pix *CubeLineSegmenter::Pixa2Pix(Pixa *pixa, Box **dest_box) {
|
||||
return Pixa2Pix(pixa, dest_box, 0, pixa->n);
|
||||
}
|
||||
|
||||
// merges a number of lines into one line given a bounding box and a mask
|
||||
bool CubeLineSegmenter::MergeLine(Pix *line_mask_pix, Box *line_box,
|
||||
Pixa *lines, Boxaa *lines_con_comps) {
|
||||
// compute the connected components of the lines to be merged
|
||||
Pixa *small_con_comps_pix;
|
||||
Boxa *small_line_con_comps = ComputeLineConComps(line_mask_pix,
|
||||
line_box, &small_con_comps_pix);
|
||||
|
||||
if (small_line_con_comps == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// for each connected component
|
||||
for (int con = 0; con < small_line_con_comps->n; con++) {
|
||||
Box *small_con_comp_box = small_line_con_comps->box[con];
|
||||
int best_line = -1,
|
||||
best_dist = INT_MAX,
|
||||
small_box_right = small_con_comp_box->x + small_con_comp_box->w,
|
||||
small_box_bottom = small_con_comp_box->y + small_con_comp_box->h;
|
||||
|
||||
// for each valid line
|
||||
for (int line = 0; line < lines->n; line++) {
|
||||
if (SmallLine(lines->boxa->box[line]) == true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// for all the connected components in the line
|
||||
Boxa *line_con_comps = lines_con_comps->boxa[line];
|
||||
|
||||
for (int lcon = 0; lcon < line_con_comps->n; lcon++) {
|
||||
Box *con_comp_box = line_con_comps->box[lcon];
|
||||
int xdist,
|
||||
ydist,
|
||||
box_right = con_comp_box->x + con_comp_box->w,
|
||||
box_bottom = con_comp_box->y + con_comp_box->h;
|
||||
|
||||
xdist = MAX(small_con_comp_box->x, con_comp_box->x) -
|
||||
MIN(small_box_right, box_right);
|
||||
|
||||
ydist = MAX(small_con_comp_box->y, con_comp_box->y) -
|
||||
MIN(small_box_bottom, box_bottom);
|
||||
|
||||
// if there is an overlap in x-direction
|
||||
if (xdist <= 0) {
|
||||
if (best_line == -1 || ydist < best_dist) {
|
||||
best_dist = ydist;
|
||||
best_line = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the distance is too big, do not merged
|
||||
if (best_line != -1 && best_dist < est_alef_hgt_) {
|
||||
// add the pix to the best line
|
||||
Pix *new_line = PixUnion(lines->pix[best_line],
|
||||
lines->boxa->box[best_line],
|
||||
small_con_comps_pix->pix[con], small_con_comp_box);
|
||||
|
||||
if (new_line == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pixDestroy(&lines->pix[best_line]);
|
||||
lines->pix[best_line] = new_line;
|
||||
}
|
||||
}
|
||||
|
||||
pixaDestroy(&small_con_comps_pix);
|
||||
boxaDestroy(&small_line_con_comps);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Creates new set of lines from the computed columns
|
||||
bool CubeLineSegmenter::AddLines(Pixa *lines) {
|
||||
// create an array that will hold the bounding boxes
|
||||
// of the concomps belonging to each line
|
||||
Boxaa *lines_con_comps = boxaaCreate(lines->n);
|
||||
if (lines_con_comps == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int line = 0; line < lines->n; line++) {
|
||||
// if the line is not valid
|
||||
if (ValidLine(lines->pix[line], lines->boxa->box[line]) == false) {
|
||||
// split it
|
||||
Pixa *split_lines = SplitLine(lines->pix[line],
|
||||
lines->boxa->box[line]);
|
||||
|
||||
// remove the old line
|
||||
if (pixaRemovePix(lines, line) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
line--;
|
||||
|
||||
if (split_lines == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// add the split lines instead and move the pointer
|
||||
for (int s_line = 0; s_line < split_lines->n; s_line++) {
|
||||
Pix *sp_line = pixaGetPix(split_lines, s_line, L_CLONE);
|
||||
Box *sp_box = boxaGetBox(split_lines->boxa, s_line, L_CLONE);
|
||||
|
||||
if (sp_line == NULL || sp_box == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// insert the new line
|
||||
if (pixaInsertPix(lines, ++line, sp_line, sp_box) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// remove the split lines
|
||||
pixaDestroy(&split_lines);
|
||||
}
|
||||
}
|
||||
|
||||
// compute the concomps bboxes of each line
|
||||
for (int line = 0; line < lines->n; line++) {
|
||||
Boxa *line_con_comps = ComputeLineConComps(lines->pix[line],
|
||||
lines->boxa->box[line], NULL);
|
||||
|
||||
if (line_con_comps == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// insert it into the boxaa array
|
||||
if (boxaaAddBoxa(lines_con_comps, line_con_comps, L_INSERT) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// post process the lines:
|
||||
// merge the contents of "small" lines info legitimate lines
|
||||
for (int line = 0; line < lines->n; line++) {
|
||||
// a small line detected
|
||||
if (SmallLine(lines->boxa->box[line]) == true) {
|
||||
// merge its components to one of the valid lines
|
||||
if (MergeLine(lines->pix[line], lines->boxa->box[line],
|
||||
lines, lines_con_comps) == true) {
|
||||
// remove the small line
|
||||
if (pixaRemovePix(lines, line) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (boxaaRemoveBoxa(lines_con_comps, line) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
line--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boxaaDestroy(&lines_con_comps);
|
||||
|
||||
// add the pix masks
|
||||
if (pixaaAddPixa(columns_, lines, L_INSERT) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Index the specific pixa using RTL reading order
|
||||
int *CubeLineSegmenter::IndexRTL(Pixa *pixa) {
|
||||
int *pix_index = new int[pixa->n];
|
||||
if (pix_index == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int pix = 0; pix < pixa->n; pix++) {
|
||||
pix_index[pix] = pix;
|
||||
}
|
||||
|
||||
for (int ipix = 0; ipix < pixa->n; ipix++) {
|
||||
for (int jpix = ipix + 1; jpix < pixa->n; jpix++) {
|
||||
Box *ipix_box = pixa->boxa->box[pix_index[ipix]],
|
||||
*jpix_box = pixa->boxa->box[pix_index[jpix]];
|
||||
|
||||
// swap?
|
||||
if ((ipix_box->x + ipix_box->w) < (jpix_box->x + jpix_box->w)) {
|
||||
int temp = pix_index[ipix];
|
||||
pix_index[ipix] = pix_index[jpix];
|
||||
pix_index[jpix] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pix_index;
|
||||
}
|
||||
|
||||
// Performs line segmentation
|
||||
bool CubeLineSegmenter::LineSegment() {
|
||||
// Use full image morphology to find columns
|
||||
// This only works for simple layouts where each column
|
||||
// of text extends the full height of the input image.
|
||||
Pix *pix_temp1 = pixMorphCompSequence(img_, "c5.500", 0);
|
||||
if (pix_temp1 == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mask with a single component over each column
|
||||
Pixa *pixam;
|
||||
Boxa *boxa = pixConnComp(pix_temp1, &pixam, 8);
|
||||
|
||||
if (boxa == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int init_morph_min_hgt = kLineSepMorphMinHgt;
|
||||
char sequence_str[16];
|
||||
sprintf(sequence_str, "c100.%d", init_morph_min_hgt);
|
||||
|
||||
// Use selective region-based morphology to get the textline mask.
|
||||
Pixa *pixad = pixaMorphSequenceByRegion(img_, pixam, sequence_str, 0, 0);
|
||||
if (pixad == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// for all columns
|
||||
int col_cnt = boxaGetCount(boxa);
|
||||
|
||||
// create columns
|
||||
columns_ = pixaaCreate(col_cnt);
|
||||
if (columns_ == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// index columns based on readind order (RTL)
|
||||
int *col_order = IndexRTL(pixad);
|
||||
if (col_order == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
line_cnt_ = 0;
|
||||
|
||||
for (int col_idx = 0; col_idx < col_cnt; col_idx++) {
|
||||
int col = col_order[col_idx];
|
||||
|
||||
// get the pix and box corresponding to the column
|
||||
Pix *pixt3 = pixaGetPix(pixad, col, L_CLONE);
|
||||
if (pixt3 == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Box *col_box = pixad->boxa->box[col];
|
||||
|
||||
Pixa *pixac;
|
||||
Boxa *boxa2 = pixConnComp(pixt3, &pixac, 8);
|
||||
if (boxa2 == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// offset the boxes by the column box
|
||||
for (int line = 0; line < pixac->n; line++) {
|
||||
pixac->boxa->box[line]->x += col_box->x;
|
||||
pixac->boxa->box[line]->y += col_box->y;
|
||||
}
|
||||
|
||||
// add the lines
|
||||
if (AddLines(pixac) == true) {
|
||||
if (pixaaAddBox(columns_, col_box, L_CLONE) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pixDestroy(&pixt3);
|
||||
boxaDestroy(&boxa2);
|
||||
|
||||
line_cnt_ += columns_->pixa[col_idx]->n;
|
||||
}
|
||||
|
||||
pixaDestroy(&pixam);
|
||||
pixaDestroy(&pixad);
|
||||
boxaDestroy(&boxa);
|
||||
|
||||
delete []col_order;
|
||||
pixDestroy(&pix_temp1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Estimate the paramters of the font(s) used in the page
|
||||
bool CubeLineSegmenter::EstimateFontParams() {
|
||||
int hgt_hist[kHgtBins];
|
||||
int max_hgt;
|
||||
double mean_hgt;
|
||||
|
||||
// init hgt histogram of concomps
|
||||
memset(hgt_hist, 0, sizeof(hgt_hist));
|
||||
|
||||
// compute max hgt
|
||||
max_hgt = 0;
|
||||
|
||||
for (int con = 0; con < con_comps_->n; con++) {
|
||||
// skip conn comps that are too long or too wide
|
||||
if (con_comps_->boxa->box[con]->h > kMaxConnCompHgt ||
|
||||
con_comps_->boxa->box[con]->w > kMaxConnCompWid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
max_hgt = MAX(max_hgt, con_comps_->boxa->box[con]->h);
|
||||
}
|
||||
|
||||
if (max_hgt <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// init hgt histogram of concomps
|
||||
memset(hgt_hist, 0, sizeof(hgt_hist));
|
||||
|
||||
// compute histogram
|
||||
mean_hgt = 0.0;
|
||||
for (int con = 0; con < con_comps_->n; con++) {
|
||||
// skip conn comps that are too long or too wide
|
||||
if (con_comps_->boxa->box[con]->h > kMaxConnCompHgt ||
|
||||
con_comps_->boxa->box[con]->w > kMaxConnCompWid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int bin = static_cast<int>(kHgtBins * con_comps_->boxa->box[con]->h /
|
||||
max_hgt);
|
||||
bin = MIN(bin, kHgtBins - 1);
|
||||
hgt_hist[bin]++;
|
||||
mean_hgt += con_comps_->boxa->box[con]->h;
|
||||
}
|
||||
|
||||
mean_hgt /= con_comps_->n;
|
||||
|
||||
// find the top 2 bins
|
||||
int idx[kHgtBins];
|
||||
|
||||
for (int bin = 0; bin < kHgtBins; bin++) {
|
||||
idx[bin] = bin;
|
||||
}
|
||||
|
||||
for (int ibin = 0; ibin < 2; ibin++) {
|
||||
for (int jbin = ibin + 1; jbin < kHgtBins; jbin++) {
|
||||
if (hgt_hist[idx[ibin]] < hgt_hist[idx[jbin]]) {
|
||||
int swap = idx[ibin];
|
||||
idx[ibin] = idx[jbin];
|
||||
idx[jbin] = swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// emperically, we found out that the 2 highest freq bins correspond
|
||||
// respectively to the dot and alef
|
||||
est_dot_hgt_ = (1.0 * (idx[0] + 1) * max_hgt / kHgtBins);
|
||||
est_alef_hgt_ = (1.0 * (idx[1] + 1) * max_hgt / kHgtBins);
|
||||
|
||||
// as a sanity check the dot hgt must be significanly lower than alef
|
||||
if (est_alef_hgt_ < (est_dot_hgt_ * 2)) {
|
||||
// use max_hgt to estimate instead
|
||||
est_alef_hgt_ = mean_hgt * 1.5;
|
||||
est_dot_hgt_ = est_alef_hgt_ / 5.0;
|
||||
}
|
||||
|
||||
est_alef_hgt_ = MAX(est_alef_hgt_, est_dot_hgt_ * 4.0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// clean up the image
|
||||
Pix *CubeLineSegmenter::CleanUp(Pix *orig_img) {
|
||||
// get rid of long horizontal lines
|
||||
Pix *pix_temp0 = pixMorphCompSequence(orig_img, "o300.2", 0);
|
||||
pixXor(pix_temp0, pix_temp0, orig_img);
|
||||
|
||||
// get rid of long vertical lines
|
||||
Pix *pix_temp1 = pixMorphCompSequence(pix_temp0, "o2.300", 0);
|
||||
pixXor(pix_temp1, pix_temp1, pix_temp0);
|
||||
|
||||
pixDestroy(&pix_temp0);
|
||||
|
||||
// detect connected components
|
||||
Pixa *con_comps;
|
||||
Boxa *boxa = pixConnComp(pix_temp1, &con_comps, 8);
|
||||
if (boxa == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// detect and remove suspicious conn comps
|
||||
for (int con = 0; con < con_comps->n; con++) {
|
||||
Box *box = boxa->box[con];
|
||||
|
||||
// remove if suspc. conn comp
|
||||
if ((box->w > (box->h * kMaxHorzAspectRatio)) ||
|
||||
(box->h > (box->w * kMaxVertAspectRatio)) ||
|
||||
(box->w < kMinWid && box->h < kMinHgt)) {
|
||||
pixRasterop(pix_temp1, box->x, box->y, box->w, box->h,
|
||||
PIX_SRC ^ PIX_DST, con_comps->pix[con], 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
pixaDestroy(&con_comps);
|
||||
boxaDestroy(&boxa);
|
||||
|
||||
return pix_temp1;
|
||||
}
|
||||
|
||||
// Init the page segmenter
|
||||
bool CubeLineSegmenter::Init() {
|
||||
if (init_ == true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (orig_img_ == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// call the internal line segmentation
|
||||
return FindLines();
|
||||
}
|
||||
|
||||
// return the pix mask and box of a specific line
|
||||
Pix *CubeLineSegmenter::Line(int line, Box **line_box) {
|
||||
if (init_ == false && Init() == false) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (line < 0 || line >= line_cnt_) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(*line_box) = lines_pixa_->boxa->box[line];
|
||||
return lines_pixa_->pix[line];
|
||||
}
|
||||
|
||||
// Implements a basic rudimentary layout analysis based on Leptonica
|
||||
// works OK for Arabic. For other languages, the function TesseractPageAnalysis
|
||||
// should be called instead.
|
||||
bool CubeLineSegmenter::FindLines() {
|
||||
// convert the image to gray scale if necessary
|
||||
Pix *gray_scale_img = NULL;
|
||||
if (orig_img_->d != 2 && orig_img_->d != 8) {
|
||||
gray_scale_img = pixConvertTo8(orig_img_, false);
|
||||
if (gray_scale_img == NULL) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
gray_scale_img = orig_img_;
|
||||
}
|
||||
|
||||
// threshold image
|
||||
Pix *thresholded_img;
|
||||
thresholded_img = pixThresholdToBinary(gray_scale_img, 128);
|
||||
// free the gray scale image if necessary
|
||||
if (gray_scale_img != orig_img_) {
|
||||
pixDestroy(&gray_scale_img);
|
||||
}
|
||||
// bail-out if thresholding failed
|
||||
if (thresholded_img == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// deskew
|
||||
Pix *deskew_img = pixDeskew(thresholded_img, 2);
|
||||
if (deskew_img == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pixDestroy(&thresholded_img);
|
||||
|
||||
img_ = CleanUp(deskew_img);
|
||||
pixDestroy(&deskew_img);
|
||||
if (img_ == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pixDestroy(&deskew_img);
|
||||
|
||||
// compute connected components
|
||||
Boxa *boxa = pixConnComp(img_, &con_comps_, 8);
|
||||
if (boxa == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boxaDestroy(&boxa);
|
||||
|
||||
// estimate dot and alef hgts
|
||||
if (EstimateFontParams() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// perform line segmentation
|
||||
if (LineSegment() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// success
|
||||
init_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
/******************************************************************************
|
||||
** 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 "extract.h"
|
||||
#include "efio.h"
|
||||
#include "featdefs.h"
|
||||
#include "callcpp.h"
|
||||
#include "chartoname.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define MAXFILENAME 80
|
||||
#define MAXMATCHES 10
|
||||
|
||||
static const char kUnknownFontName[] = "UnknownFont";
|
||||
|
||||
STRING_VAR(classify_font_name, kUnknownFontName,
|
||||
"Default font name to be used in training");
|
||||
|
||||
/**----------------------------------------------------------------------------
|
||||
Global Data Definitions and Declarations
|
||||
----------------------------------------------------------------------------**/
|
||||
/* name of current image file being processed */
|
||||
extern char imagefile[];
|
||||
|
||||
/**----------------------------------------------------------------------------
|
||||
Public Code
|
||||
----------------------------------------------------------------------------**/
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void LearnBlob(const FEATURE_DEFS_STRUCT &FeatureDefs, const STRING& filename,
|
||||
TBLOB * Blob, const DENORM& denorm, const char* BlobText) {
|
||||
/*
|
||||
** 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
|
||||
** classify_font_name
|
||||
** 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 TRAIN_SUFFIX ".tr"
|
||||
static FILE *FeatureFile = NULL;
|
||||
STRING Filename(filename);
|
||||
|
||||
// If no fontname was set, try to extract it from the filename
|
||||
STRING CurrFontName = classify_font_name;
|
||||
if (CurrFontName == kUnknownFontName) {
|
||||
// filename is expected to be of the form [lang].[fontname].exp[num]
|
||||
// The [lang], [fontname] and [num] fields should not have '.' characters.
|
||||
const char *basename = strrchr(filename.string(), '/');
|
||||
const char *firstdot = strchr(basename ? basename : filename.string(), '.');
|
||||
const char *lastdot = strrchr(filename.string(), '.');
|
||||
if (firstdot != lastdot && firstdot != NULL && lastdot != NULL) {
|
||||
++firstdot;
|
||||
CurrFontName = firstdot;
|
||||
CurrFontName[lastdot - firstdot] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
Filename += TRAIN_SUFFIX;
|
||||
FeatureFile = Efopen(Filename.string(), "wb");
|
||||
cprintf("TRAINING ... Font name = %s\n", CurrFontName.string());
|
||||
}
|
||||
|
||||
LearnBlob(FeatureDefs, FeatureFile, Blob, denorm, BlobText,
|
||||
CurrFontName.string());
|
||||
} // LearnBlob
|
||||
|
||||
void LearnBlob(const FEATURE_DEFS_STRUCT &FeatureDefs, FILE* FeatureFile,
|
||||
TBLOB* Blob, const DENORM& denorm,
|
||||
const char* BlobText, const char* FontName) {
|
||||
CHAR_DESC CharDesc;
|
||||
|
||||
ASSERT_HOST(FeatureFile != NULL);
|
||||
|
||||
CharDesc = ExtractBlobFeatures(FeatureDefs, denorm, Blob);
|
||||
if (CharDesc == NULL) {
|
||||
cprintf("LearnBLob: CharDesc was NULL. Aborting.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ValidCharDescription(FeatureDefs, CharDesc)) {
|
||||
// label the features with a class name and font name
|
||||
fprintf(FeatureFile, "\n%s %s\n", FontName, BlobText);
|
||||
|
||||
// write micro-features to file and clean up
|
||||
WriteCharDescription(FeatureDefs, FeatureFile, CharDesc);
|
||||
} else {
|
||||
tprintf("Blob learned was invalid!\n");
|
||||
}
|
||||
FreeCharDescription(CharDesc);
|
||||
|
||||
} // LearnBlob
|
|
@ -1,45 +0,0 @@
|
|||
/******************************************************************************
|
||||
** 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
|
||||
----------------------------------------------------------------------------**/
|
||||
|
||||
/*
|
||||
#if defined(__STDC__) || defined(__cplusplus)
|
||||
# define _ARGS(s) s
|
||||
#else
|
||||
# define _ARGS(s) ()
|
||||
#endif*/
|
||||
|
||||
/* cutoffs.c
|
||||
void ReadNewCutoffs
|
||||
_ARGS((char *Filename,
|
||||
CLASS_CUTOFF_ARRAY Cutoffs));
|
||||
#undef _ARGS
|
||||
*/
|
||||
#endif
|
|
@ -1,146 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////
|
||||
// File: svmnode.cpp
|
||||
// description_: ScrollView Menu Node
|
||||
// Author: Joern Wanke
|
||||
// Created: Thu Nov 29 2007
|
||||
//
|
||||
// (C) Copyright 2007, 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.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// A SVMenuNode is an entity which contains the mapping from a menu entry on
|
||||
// the server side to the corresponding associated commands on the client.
|
||||
// It is designed to be a tree structure with a root node, which can then be
|
||||
// used to generate the appropriate messages to the server to display the
|
||||
// menu structure there.
|
||||
// A SVMenuNode can both be used in the context_ of popup menus as well as
|
||||
// menu bars.
|
||||
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include "svmnode.h"
|
||||
|
||||
// Include automatically generated configuration file if running autoconf.
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config_auto.h"
|
||||
#endif
|
||||
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
|
||||
#include "scrollview.h"
|
||||
|
||||
// Create the empty root menu node. with just a caption. All other nodes should
|
||||
// be added to this or one of the submenus.
|
||||
SVMenuNode::SVMenuNode() {
|
||||
cmd_event_ = -1;
|
||||
text_ = NULL;
|
||||
child_ = NULL;
|
||||
next_ = NULL;
|
||||
parent_ = NULL;
|
||||
toggle_value_ = false;
|
||||
is_check_box_entry_ = false;
|
||||
value_ = NULL;
|
||||
description_ = NULL;
|
||||
}
|
||||
|
||||
SVMenuNode::~SVMenuNode() {
|
||||
delete[] text_;
|
||||
// delete[] description_;
|
||||
}
|
||||
|
||||
// Create a new sub menu node with just a caption. This is used to create
|
||||
// nodes which act as parent nodes to other nodes (e.g. submenus).
|
||||
SVMenuNode* SVMenuNode::AddChild(const char* txt) {
|
||||
SVMenuNode* s = new SVMenuNode(-1, txt, false, false, NULL, NULL);
|
||||
this->AddChild(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Create a "normal" menu node which is associated with a command event.
|
||||
void SVMenuNode::AddChild(const char* txt, int command_event) {
|
||||
this->AddChild(new SVMenuNode(command_event, txt, false, false, NULL, NULL));
|
||||
}
|
||||
|
||||
// Create a menu node with an associated value (which might be changed
|
||||
// through the gui).
|
||||
void SVMenuNode::AddChild(const char* txt, int command_event,
|
||||
const char* val) {
|
||||
this->AddChild(new SVMenuNode(command_event, txt, false, false, val, NULL));
|
||||
}
|
||||
|
||||
// Create a menu node with an associated value and description_.
|
||||
void SVMenuNode::AddChild(const char* txt, int command_event, const char* val,
|
||||
const char* desc) {
|
||||
this->AddChild(new SVMenuNode(command_event, txt, false, false, val, desc));
|
||||
}
|
||||
|
||||
// Create a flag menu node.
|
||||
void SVMenuNode::AddChild(const char* txt, int command_event, int tv) {
|
||||
this->AddChild(new SVMenuNode(command_event, txt, tv, true, NULL, NULL));
|
||||
}
|
||||
|
||||
// Convenience function called from the different constructors to initialize
|
||||
// the different values of the menu node.
|
||||
SVMenuNode::SVMenuNode(int command_event, const char* txt,
|
||||
int tv, bool check_box_entry, const char* val,
|
||||
const char* desc) {
|
||||
cmd_event_ = command_event;
|
||||
|
||||
text_ = new char[strlen(txt) + 1];
|
||||
strncpy(text_, txt, strlen(txt));
|
||||
text_[strlen(txt)] = '\0';
|
||||
|
||||
value_ = val;
|
||||
description_ = desc;
|
||||
|
||||
child_ = NULL;
|
||||
next_ = NULL;
|
||||
parent_ = NULL;
|
||||
toggle_value_ = tv != 0;
|
||||
is_check_box_entry_ = check_box_entry;
|
||||
}
|
||||
|
||||
// Add a child node to this menu node.
|
||||
void SVMenuNode::AddChild(SVMenuNode* svmn) {
|
||||
svmn->parent_ = this;
|
||||
// No children yet.
|
||||
if (child_ == NULL) {
|
||||
child_ = svmn;
|
||||
} else {
|
||||
SVMenuNode* cur = child_;
|
||||
while (cur->next_ != NULL) { cur = cur->next_; }
|
||||
cur->next_ = svmn;
|
||||
}
|
||||
}
|
||||
|
||||
// Build a menu structure for the server and send the necessary messages.
|
||||
// Should be called on the root node. If menu_bar is true, a menu_bar menu
|
||||
// is built (e.g. on top of the window), if it is false a popup menu is
|
||||
// built which gets shown by right clicking on the window.
|
||||
// Deletes itself afterwards.
|
||||
void SVMenuNode::BuildMenu(ScrollView* sv, bool menu_bar) {
|
||||
if ((parent_ != NULL) && (menu_bar)) {
|
||||
if (is_check_box_entry_) {
|
||||
sv->MenuItem(parent_->text_, text_, cmd_event_, toggle_value_);
|
||||
} else { sv->MenuItem(parent_->text_, text_, cmd_event_); }
|
||||
} else if ((parent_ != NULL) && (!menu_bar)) {
|
||||
if (description_ != NULL) { sv->PopupItem(parent_->text_, text_,
|
||||
cmd_event_, value_, description_);
|
||||
} else { sv->PopupItem(parent_->text_, text_); }
|
||||
}
|
||||
if (child_ != NULL) { child_->BuildMenu(sv, menu_bar); delete child_; }
|
||||
if (next_ != NULL) { next_->BuildMenu(sv, menu_bar); delete next_; }
|
||||
}
|
||||
|
||||
#endif // GRAPHICS_DISABLED
|
|
@ -1,560 +0,0 @@
|
|||
/*
|
||||
* searchtools.js_t
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilties for the full-text search.
|
||||
*
|
||||
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* helper function to return a node containing the
|
||||
* search summary for a given text. keywords is a list
|
||||
* of stemmed words, hlwords is the list of normal, unstemmed
|
||||
* words. the first one is used to find the occurance, the
|
||||
* latter for highlighting it.
|
||||
*/
|
||||
|
||||
jQuery.makeSearchSummary = function(text, keywords, hlwords) {
|
||||
var textLower = text.toLowerCase();
|
||||
var start = 0;
|
||||
$.each(keywords, function() {
|
||||
var i = textLower.indexOf(this.toLowerCase());
|
||||
if (i > -1)
|
||||
start = i;
|
||||
});
|
||||
start = Math.max(start - 120, 0);
|
||||
var excerpt = ((start > 0) ? '...' : '') +
|
||||
$.trim(text.substr(start, 240)) +
|
||||
((start + 240 - text.length) ? '...' : '');
|
||||
var rv = $('<div class="context"></div>').text(excerpt);
|
||||
$.each(hlwords, function() {
|
||||
rv = rv.highlightText(this, 'highlighted');
|
||||
});
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
var Stemmer = function() {
|
||||
|
||||
var step2list = {
|
||||
ational: 'ate',
|
||||
tional: 'tion',
|
||||
enci: 'ence',
|
||||
anci: 'ance',
|
||||
izer: 'ize',
|
||||
bli: 'ble',
|
||||
alli: 'al',
|
||||
entli: 'ent',
|
||||
eli: 'e',
|
||||
ousli: 'ous',
|
||||
ization: 'ize',
|
||||
ation: 'ate',
|
||||
ator: 'ate',
|
||||
alism: 'al',
|
||||
iveness: 'ive',
|
||||
fulness: 'ful',
|
||||
ousness: 'ous',
|
||||
aliti: 'al',
|
||||
iviti: 'ive',
|
||||
biliti: 'ble',
|
||||
logi: 'log'
|
||||
};
|
||||
|
||||
var step3list = {
|
||||
icate: 'ic',
|
||||
ative: '',
|
||||
alize: 'al',
|
||||
iciti: 'ic',
|
||||
ical: 'ic',
|
||||
ful: '',
|
||||
ness: ''
|
||||
};
|
||||
|
||||
var c = "[^aeiou]"; // consonant
|
||||
var v = "[aeiouy]"; // vowel
|
||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
||||
var V = v + "[aeiou]*"; // vowel sequence
|
||||
|
||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
||||
|
||||
this.stemWord = function (w) {
|
||||
var stem;
|
||||
var suffix;
|
||||
var firstch;
|
||||
var origword = w;
|
||||
|
||||
if (w.length < 3)
|
||||
return w;
|
||||
|
||||
var re;
|
||||
var re2;
|
||||
var re3;
|
||||
var re4;
|
||||
|
||||
firstch = w.substr(0,1);
|
||||
if (firstch == "y")
|
||||
w = firstch.toUpperCase() + w.substr(1);
|
||||
|
||||
// Step 1a
|
||||
re = /^(.+?)(ss|i)es$/;
|
||||
re2 = /^(.+?)([^s])s$/;
|
||||
|
||||
if (re.test(w))
|
||||
w = w.replace(re,"$1$2");
|
||||
else if (re2.test(w))
|
||||
w = w.replace(re2,"$1$2");
|
||||
|
||||
// Step 1b
|
||||
re = /^(.+?)eed$/;
|
||||
re2 = /^(.+?)(ed|ing)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(fp[1])) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1];
|
||||
re2 = new RegExp(s_v);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
re2 = /(at|bl|iz)$/;
|
||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re2.test(w))
|
||||
w = w + "e";
|
||||
else if (re3.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
else if (re4.test(w))
|
||||
w = w + "e";
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1c
|
||||
re = /^(.+?)y$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(s_v);
|
||||
if (re.test(stem))
|
||||
w = stem + "i";
|
||||
}
|
||||
|
||||
// Step 2
|
||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step2list[suffix];
|
||||
}
|
||||
|
||||
// Step 3
|
||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step3list[suffix];
|
||||
}
|
||||
|
||||
// Step 4
|
||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
||||
re2 = /^(.+?)(s|t)(ion)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
if (re.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1] + fp[2];
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re2.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
|
||||
// Step 5
|
||||
re = /^(.+?)e$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
re2 = new RegExp(meq1);
|
||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
|
||||
w = stem;
|
||||
}
|
||||
re = /ll$/;
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re.test(w) && re2.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
|
||||
// and turn initial Y back to y
|
||||
if (firstch == "y")
|
||||
w = firstch.toLowerCase() + w.substr(1);
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search Module
|
||||
*/
|
||||
var Search = {
|
||||
|
||||
_index : null,
|
||||
_queued_query : null,
|
||||
_pulse_status : -1,
|
||||
|
||||
init : function() {
|
||||
var params = $.getQueryParameters();
|
||||
if (params.q) {
|
||||
var query = params.q[0];
|
||||
$('input[name="q"]')[0].value = query;
|
||||
this.performSearch(query);
|
||||
}
|
||||
},
|
||||
|
||||
loadIndex : function(url) {
|
||||
$.ajax({type: "GET", url: url, data: null, success: null,
|
||||
dataType: "script", cache: true});
|
||||
},
|
||||
|
||||
setIndex : function(index) {
|
||||
var q;
|
||||
this._index = index;
|
||||
if ((q = this._queued_query) !== null) {
|
||||
this._queued_query = null;
|
||||
Search.query(q);
|
||||
}
|
||||
},
|
||||
|
||||
hasIndex : function() {
|
||||
return this._index !== null;
|
||||
},
|
||||
|
||||
deferQuery : function(query) {
|
||||
this._queued_query = query;
|
||||
},
|
||||
|
||||
stopPulse : function() {
|
||||
this._pulse_status = 0;
|
||||
},
|
||||
|
||||
startPulse : function() {
|
||||
if (this._pulse_status >= 0)
|
||||
return;
|
||||
function pulse() {
|
||||
Search._pulse_status = (Search._pulse_status + 1) % 4;
|
||||
var dotString = '';
|
||||
for (var i = 0; i < Search._pulse_status; i++)
|
||||
dotString += '.';
|
||||
Search.dots.text(dotString);
|
||||
if (Search._pulse_status > -1)
|
||||
window.setTimeout(pulse, 500);
|
||||
};
|
||||
pulse();
|
||||
},
|
||||
|
||||
/**
|
||||
* perform a search for something
|
||||
*/
|
||||
performSearch : function(query) {
|
||||
// create the required interface elements
|
||||
this.out = $('#search-results');
|
||||
this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
|
||||
this.dots = $('<span></span>').appendTo(this.title);
|
||||
this.status = $('<p style="display: none"></p>').appendTo(this.out);
|
||||
this.output = $('<ul class="search"/>').appendTo(this.out);
|
||||
|
||||
$('#search-progress').text(_('Preparing search...'));
|
||||
this.startPulse();
|
||||
|
||||
// index already loaded, the browser was quick!
|
||||
if (this.hasIndex())
|
||||
this.query(query);
|
||||
else
|
||||
this.deferQuery(query);
|
||||
},
|
||||
|
||||
query : function(query) {
|
||||
var stopwords = ["and","then","into","it","as","are","in","if","for","no","there","their","was","is","be","to","that","but","they","not","such","with","by","a","on","these","of","will","this","near","the","or","at"];
|
||||
|
||||
// Stem the searchterms and add them to the correct list
|
||||
var stemmer = new Stemmer();
|
||||
var searchterms = [];
|
||||
var excluded = [];
|
||||
var hlterms = [];
|
||||
var tmp = query.split(/\s+/);
|
||||
var objectterms = [];
|
||||
for (var i = 0; i < tmp.length; i++) {
|
||||
if (tmp[i] != "") {
|
||||
objectterms.push(tmp[i].toLowerCase());
|
||||
}
|
||||
|
||||
if ($u.indexOf(stopwords, tmp[i]) != -1 || tmp[i].match(/^\d+$/) ||
|
||||
tmp[i] == "") {
|
||||
// skip this "word"
|
||||
continue;
|
||||
}
|
||||
// stem the word
|
||||
var word = stemmer.stemWord(tmp[i]).toLowerCase();
|
||||
// select the correct list
|
||||
if (word[0] == '-') {
|
||||
var toAppend = excluded;
|
||||
word = word.substr(1);
|
||||
}
|
||||
else {
|
||||
var toAppend = searchterms;
|
||||
hlterms.push(tmp[i].toLowerCase());
|
||||
}
|
||||
// only add if not already in the list
|
||||
if (!$.contains(toAppend, word))
|
||||
toAppend.push(word);
|
||||
};
|
||||
var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
|
||||
|
||||
// console.debug('SEARCH: searching for:');
|
||||
// console.info('required: ', searchterms);
|
||||
// console.info('excluded: ', excluded);
|
||||
|
||||
// prepare search
|
||||
var filenames = this._index.filenames;
|
||||
var titles = this._index.titles;
|
||||
var terms = this._index.terms;
|
||||
var fileMap = {};
|
||||
var files = null;
|
||||
// different result priorities
|
||||
var importantResults = [];
|
||||
var objectResults = [];
|
||||
var regularResults = [];
|
||||
var unimportantResults = [];
|
||||
$('#search-progress').empty();
|
||||
|
||||
// lookup as object
|
||||
for (var i = 0; i < objectterms.length; i++) {
|
||||
var others = [].concat(objectterms.slice(0,i),
|
||||
objectterms.slice(i+1, objectterms.length))
|
||||
var results = this.performObjectSearch(objectterms[i], others);
|
||||
// Assume first word is most likely to be the object,
|
||||
// other words more likely to be in description.
|
||||
// Therefore put matches for earlier words first.
|
||||
// (Results are eventually used in reverse order).
|
||||
objectResults = results[0].concat(objectResults);
|
||||
importantResults = results[1].concat(importantResults);
|
||||
unimportantResults = results[2].concat(unimportantResults);
|
||||
}
|
||||
|
||||
// perform the search on the required terms
|
||||
for (var i = 0; i < searchterms.length; i++) {
|
||||
var word = searchterms[i];
|
||||
// no match but word was a required one
|
||||
if ((files = terms[word]) == null)
|
||||
break;
|
||||
if (files.length == undefined) {
|
||||
files = [files];
|
||||
}
|
||||
// create the mapping
|
||||
for (var j = 0; j < files.length; j++) {
|
||||
var file = files[j];
|
||||
if (file in fileMap)
|
||||
fileMap[file].push(word);
|
||||
else
|
||||
fileMap[file] = [word];
|
||||
}
|
||||
}
|
||||
|
||||
// now check if the files don't contain excluded terms
|
||||
for (var file in fileMap) {
|
||||
var valid = true;
|
||||
|
||||
// check if all requirements are matched
|
||||
if (fileMap[file].length != searchterms.length)
|
||||
continue;
|
||||
|
||||
// ensure that none of the excluded terms is in the
|
||||
// search result.
|
||||
for (var i = 0; i < excluded.length; i++) {
|
||||
if (terms[excluded[i]] == file ||
|
||||
$.contains(terms[excluded[i]] || [], file)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have still a valid result we can add it
|
||||
// to the result list
|
||||
if (valid)
|
||||
regularResults.push([filenames[file], titles[file], '', null]);
|
||||
}
|
||||
|
||||
// delete unused variables in order to not waste
|
||||
// memory until list is retrieved completely
|
||||
delete filenames, titles, terms;
|
||||
|
||||
// now sort the regular results descending by title
|
||||
regularResults.sort(function(a, b) {
|
||||
var left = a[1].toLowerCase();
|
||||
var right = b[1].toLowerCase();
|
||||
return (left > right) ? -1 : ((left < right) ? 1 : 0);
|
||||
});
|
||||
|
||||
// combine all results
|
||||
var results = unimportantResults.concat(regularResults)
|
||||
.concat(objectResults).concat(importantResults);
|
||||
|
||||
// print the results
|
||||
var resultCount = results.length;
|
||||
function displayNextItem() {
|
||||
// results left, load the summary and display it
|
||||
if (results.length) {
|
||||
var item = results.pop();
|
||||
var listItem = $('<li style="display:none"></li>');
|
||||
if (DOCUMENTATION_OPTIONS.FILE_SUFFIX == '') {
|
||||
// dirhtml builder
|
||||
var dirname = item[0] + '/';
|
||||
if (dirname.match(/\/index\/$/)) {
|
||||
dirname = dirname.substring(0, dirname.length-6);
|
||||
} else if (dirname == 'index/') {
|
||||
dirname = '';
|
||||
}
|
||||
listItem.append($('<a/>').attr('href',
|
||||
DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
} else {
|
||||
// normal html builders
|
||||
listItem.append($('<a/>').attr('href',
|
||||
item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
}
|
||||
if (item[3]) {
|
||||
listItem.append($('<span> (' + item[3] + ')</span>'));
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
|
||||
$.get(DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' +
|
||||
item[0] + '.txt', function(data) {
|
||||
if (data != '') {
|
||||
listItem.append($.makeSearchSummary(data, searchterms, hlterms));
|
||||
Search.output.append(listItem);
|
||||
}
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}, "text");
|
||||
} else {
|
||||
// no source available, just display title
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}
|
||||
}
|
||||
// search finished, update title and status message
|
||||
else {
|
||||
Search.stopPulse();
|
||||
Search.title.text(_('Search Results'));
|
||||
if (!resultCount)
|
||||
Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
|
||||
else
|
||||
Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
|
||||
Search.status.fadeIn(500);
|
||||
}
|
||||
}
|
||||
displayNextItem();
|
||||
},
|
||||
|
||||
performObjectSearch : function(object, otherterms) {
|
||||
var filenames = this._index.filenames;
|
||||
var objects = this._index.objects;
|
||||
var objnames = this._index.objnames;
|
||||
var titles = this._index.titles;
|
||||
|
||||
var importantResults = [];
|
||||
var objectResults = [];
|
||||
var unimportantResults = [];
|
||||
|
||||
for (var prefix in objects) {
|
||||
for (var name in objects[prefix]) {
|
||||
var fullname = (prefix ? prefix + '.' : '') + name;
|
||||
if (fullname.toLowerCase().indexOf(object) > -1) {
|
||||
var match = objects[prefix][name];
|
||||
var objname = objnames[match[1]][2];
|
||||
var title = titles[match[0]];
|
||||
// If more than one term searched for, we require other words to be
|
||||
// found in the name/title/description
|
||||
if (otherterms.length > 0) {
|
||||
var haystack = (prefix + ' ' + name + ' ' +
|
||||
objname + ' ' + title).toLowerCase();
|
||||
var allfound = true;
|
||||
for (var i = 0; i < otherterms.length; i++) {
|
||||
if (haystack.indexOf(otherterms[i]) == -1) {
|
||||
allfound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allfound) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var descr = objname + _(', in ') + title;
|
||||
anchor = match[3];
|
||||
if (anchor == '')
|
||||
anchor = fullname;
|
||||
else if (anchor == '-')
|
||||
anchor = objnames[match[1]][1] + '-' + fullname;
|
||||
result = [filenames[match[0]], fullname, '#'+anchor, descr];
|
||||
switch (match[2]) {
|
||||
case 1: objectResults.push(result); break;
|
||||
case 0: importantResults.push(result); break;
|
||||
case 2: unimportantResults.push(result); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort results descending
|
||||
objectResults.sort(function(a, b) {
|
||||
return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
|
||||
});
|
||||
|
||||
importantResults.sort(function(a, b) {
|
||||
return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
|
||||
});
|
||||
|
||||
unimportantResults.sort(function(a, b) {
|
||||
return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
|
||||
});
|
||||
|
||||
return [importantResults, objectResults, unimportantResults]
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
Search.init();
|
||||
});
|
|
@ -1,479 +0,0 @@
|
|||
:version: $RCSfile: index.rst,v $ $Revision: 76e0bf38aaba $ $Date: 2011/03/22 00:48:41 $
|
||||
|
||||
.. default-role:: fs
|
||||
|
||||
=================================
|
||||
Programming with `libtesseract`
|
||||
=================================
|
||||
|
||||
To use `libtesseract` in your own application you need to include
|
||||
|Leptonica|\ ’s `allheaders.h`, and |Tesseractocr|\ ’s `baseapi.h` and
|
||||
`strngs.h`.
|
||||
|
||||
|Tesseractocr| uses `liblept` mainly for image I/O, but you can also use
|
||||
any of |Leptonica|\ ’s *many* image processing functions on ``PIX``,
|
||||
while at the same time calling ``TessBaseAPI`` methods. See the
|
||||
`Leptonica documentation <http://tpgit.github.com/UnOfficialLeptDocs/>`_
|
||||
for more details.
|
||||
|
||||
There doesn't seem to be any documentation on `api\\baseapi.h`, but it
|
||||
has extensive comments. You can also look at the :ref:`APITest` and
|
||||
:ref:`APIExamples` projects.
|
||||
|
||||
See the :ref:`APITest` project for an example of which compiler and
|
||||
linker settings you need for various build configurations. The easiest
|
||||
way to begin a new application is to just make a copy of the `APITest`
|
||||
directory. See :ref:`this step <copying_a_project>` for detailed
|
||||
instructions (skip the last step about adding :guilabel:`Project
|
||||
Dependencies`).
|
||||
|
||||
If you want to manually set the required settings, then here's the list
|
||||
of things to do:
|
||||
|
||||
1. Add the following :guilabel:`Preprocessor Definitions` when compiling
|
||||
any files that include `baseapi.h` and you are linking with the
|
||||
static library versions of `libtesseract`::
|
||||
|
||||
USE_STD_NAMESPACE
|
||||
|
||||
If you are linking with the DLL versions of `libtesseract` instead
|
||||
add::
|
||||
|
||||
USE_STD_NAMESPACE;TESSDLL_IMPORTS;CCUTIL_IMPORTS;LIBLEPT_IMPORTS
|
||||
|
||||
#. Be sure to add the following to :guilabel:`Additional Include
|
||||
Directories`::
|
||||
|
||||
C:\BuildFolder\include
|
||||
C:\BuildFolder\include\leptonica
|
||||
|
||||
C:\BuildFolder\include\tesseract or
|
||||
|
||||
<tesseract-3.0x dir> (all its sub-directories that contain header files)
|
||||
|
||||
#. Add `C:\\BuildFolder\\lib` to your :guilabel:`Additional Library
|
||||
Directories`.
|
||||
|
||||
#. In the `C:\\BuildFolder\\include` directory are two Visual Studio
|
||||
Property Sheet files::
|
||||
|
||||
tesseract_versionnumbers.vsprops
|
||||
leptonica_versionnumbers.vsprops
|
||||
|
||||
Using `tesseract_versionnumbers.vsprops` (which automatically inherits
|
||||
`leptonica_versionnumbers.vsprops`) can make it easier to specify the
|
||||
libraries you need to import. For example, when creating a staticly
|
||||
linked debug executable you can say::
|
||||
|
||||
zlib$(ZLIB_VERSION)-static-mtdll-debug.lib
|
||||
libpng$(LIBPNG_VERSION)-static-mtdll-debug.lib
|
||||
libjpeg$(LIBJPEG_VERSION)-static-mtdll-debug.lib
|
||||
giflib$(GIFLIB_VERSION)-static-mtdll-debug.lib
|
||||
libtiff$(LIBTIFF_VERSION)-static-mtdll-debug.lib
|
||||
liblept$(LIBLEPT_VERSION)-static-mtdll-debug.lib
|
||||
libtesseract$(LIBTESS_VERSION)-static-debug.lib
|
||||
|
||||
to make your application less dependent on library version numbers.
|
||||
|
||||
To add the Property Sheet to a Project, open its :guilabel:`Properties
|
||||
Pages` Dialog, and set the :guilabel:`Configuration Properties |
|
||||
General | Inherited Project Property Sheets` item to::
|
||||
|
||||
..\..\..\include\tesseract_versionnumbers.vsprops
|
||||
|
||||
Choosing :menuselection:`&View --> Oth&er Windows --> Property
|
||||
&Manager` from the menubar will let you see the Properties attached
|
||||
to each Project's configurations.
|
||||
|
||||
.. note::
|
||||
|
||||
The DLL versions of |libtess| currently only export the
|
||||
``TessBaseAPI`` C++ class from `baseapi.h`, there is no C function
|
||||
interface yet.
|
||||
|
||||
.. note::
|
||||
|
||||
The DLL versions of `libtesseract` currently only export the
|
||||
``TessBaseAPI`` and ``STRING`` classes. In theory, all you need is
|
||||
are those classes. However, if you find yourself having to manipulate
|
||||
other "internal" tesseract objects then you currently have to link
|
||||
with the **static library** versions of `libtesseract`.
|
||||
|
||||
.. warning::
|
||||
|
||||
The Release versions of |liblept|, by design, *never* print out any
|
||||
possibly helpful messages to the console. Therefore, it is highly
|
||||
recommended that you do your initial development using the Debug
|
||||
versions of |liblept|. See `Compile-time control over stderr output
|
||||
<http://tpgit.github.com/UnOfficialLeptDocs/leptonica/README.html#compile-time-control-over-stderr-output>`_
|
||||
for details.
|
||||
|
||||
<<<Need to add the URL of the zip file that contains include & lib
|
||||
directory contents for those people who don't want to build libtesseract
|
||||
themselves>>>
|
||||
|
||||
|
||||
Debugging Tips
|
||||
==============
|
||||
|
||||
Before debugging programs written with `libtesseract`, you should first
|
||||
download the latest Leptonica sources (currently
|
||||
`leptonica-1.68.tar.gz`) and VS2008 source package (`vs2008-1.68.zip`)
|
||||
from:
|
||||
|
||||
+ http://code.google.com/p/leptonica/downloads/detail?name=leptonica-1.68.tar.gz
|
||||
+ http://code.google.com/p/leptonica/downloads/detail?name=vs2008-1.68.zip
|
||||
|
||||
Unpack them to `C:\\BuildFolder` to get the following directory structure::
|
||||
|
||||
C:\BuildFolder\
|
||||
include\
|
||||
lib\
|
||||
leptonica-1.68\
|
||||
vs2008\
|
||||
tesseract-3.02\
|
||||
vs2008\
|
||||
testing\
|
||||
tessdata\
|
||||
|
||||
(see `Building the liblept library
|
||||
<http://tpgit.github.com/UnOfficialLeptDocs/vs2008/building-liblept.html>`_
|
||||
for more information)
|
||||
|
||||
|Tesseractocr| uses |Leptonica| "under the hood" for all (most? some?)
|
||||
of its image processing operations. Having the source available (and
|
||||
compiling it in debug mode) will make it easier to see what's really
|
||||
going on.
|
||||
|
||||
You might want to add
|
||||
`C:\\BuildFolder\\leptonica-1.68\\vs2008\\leptonica.vcproj` and
|
||||
`C:\\BuildFolder\\tesseract-3.02\\vs2008\\libtesseract\\libtesseract.vcproj`
|
||||
to your solution by right-clicking it and choosing :menuselection:`A&dd -->
|
||||
&Existing Project...`. This seems to make VS2008's Intellisense `work
|
||||
better
|
||||
<http://tpgit.github.com/UnOfficialLeptDocs/vs2008/building-other-programs.html#intellisense-and-liblept>`_
|
||||
when finding "external" source files.
|
||||
|
||||
Definitely create a ``TESSDATA_PREFIX``x environment variable so that it
|
||||
contains the absolute path of the directory that contains the
|
||||
``tessdata`` directory. Otherwise you'll have to put a ``tessdata``
|
||||
directory in every temporary build folder which quickly becomes painful
|
||||
(especially since tessdata has gotten very big --- 600MB!).
|
||||
|
||||
|
||||
.. _APITest:
|
||||
|
||||
APITest Sample
|
||||
==============
|
||||
|
||||
The :guilabel:`APITest` Solution contains the minimal settings needed to
|
||||
link with `libtesseract`. It demonstrates the typical situation, where
|
||||
the "external" application's source files reside *outside* of the
|
||||
`tesseract-3.0x` directory tree.
|
||||
|
||||
To build the `vs2008\\APITest` Solution, first copy it to your
|
||||
`C:\\BuildFolder` directory. This should now look like::
|
||||
|
||||
C:\BuildFolder\
|
||||
|
||||
include\
|
||||
leptonica\
|
||||
tesseract\
|
||||
|
||||
leptonica_versionnumbers.vsprops
|
||||
tesseract_versionnumbers.vsprops
|
||||
|
||||
lib\
|
||||
giflib416-static-mtdll-debug.lib
|
||||
giflib416-static-mtdll.lib
|
||||
libjpeg8c-static-mtdll-debug.lib
|
||||
libjpeg8c-static-mtdll.lib
|
||||
liblept168-static-mtdll-debug.lib
|
||||
liblept168-static-mtdll.lib
|
||||
liblept168.dll
|
||||
liblept168.lib
|
||||
liblept168d.dll
|
||||
liblept168d.lib
|
||||
libpng143-static-mtdll-debug.lib
|
||||
libpng143-static-mtdll.lib
|
||||
libtesseract302.dll
|
||||
libtesseract302.lib
|
||||
libtesseract302d.dll
|
||||
libtesseract302d.lib
|
||||
libtesseract302-static.lib
|
||||
libtesseract302-static-debug.lib
|
||||
libtiff394-static-mtdll-debug.lib
|
||||
libtiff394-static-mtdll.lib
|
||||
zlib125-static-mtdll-debug.lib
|
||||
zlib125-static-mtdll.lib
|
||||
|
||||
tesseract-3.02\
|
||||
|
||||
APITest\
|
||||
baseapitester\
|
||||
baseapitester.cpp
|
||||
baseapitester.rc
|
||||
baseapitester.vcproj
|
||||
resource.h
|
||||
stdafx.cpp
|
||||
stdafx.h
|
||||
targetver.h
|
||||
APITest.sln
|
||||
|
||||
The :guilabel:`APITest` contains just the :guilabel:`baseapitester`
|
||||
project. This was created using the VS2008 :guilabel:`Win32 Console
|
||||
Application` Project Wizard and then just copying most of
|
||||
`tesseractmain.cpp` and making minor edits. Its settings correctly refer
|
||||
to the "public" `include` and `lib` directories using relative paths.
|
||||
|
||||
It assumes that the `C:\\BuildFolder\\include` directory has been
|
||||
properly setup. See :ref:`this <copying-headers>` for more details.
|
||||
|
||||
The `C:\\BuildFolder\\lib` directory will automatically get
|
||||
`libtesseract` copied to it whenever it is built.
|
||||
|
||||
The `include\\tesseract_versionnumbers.vsprops` Property Sheet is used
|
||||
to avoid explicit library version number dependencies. Precompiled
|
||||
headers are used. :guilabel:`LIB_Release`, :guilabel:`LIB_Debug`,
|
||||
:guilabel:`DLL_Release`, and :guilabel:`DLL_Debug` build configurations
|
||||
are supported.
|
||||
|
||||
The following are the compiler command lines and linker options
|
||||
used. See `Compiling a C/C++ Program | Compiler Options
|
||||
<http://msdn.microsoft.com/en-us/library/9s7c9wdw(v=vs.90).aspx>`_ for a
|
||||
detailed explanation of these options.
|
||||
|
||||
.. _apitest-lib-release:
|
||||
|
||||
:guilabel:`LIB_Release` C/C++ :guilabel:`Command Line`::
|
||||
|
||||
/O2
|
||||
/I "." /I "..\..\include" /I "..\..\include\leptonica"
|
||||
/I "..\..\include\tesseract"
|
||||
/D "WIN32" /D "_WINDOWS" /D "NDEBUG"
|
||||
/D "USE_STD_NAMESPACE" /D "_MBCS"
|
||||
/FD /EHsc /MD /Yc"stdafx.h"
|
||||
/Fp"LIB_Release\baseapitester.pch" /Fo"LIB_Release\\"
|
||||
/Fd"LIB_Release\vc90.pdb"
|
||||
/W3 /nologo /c
|
||||
/wd4244 /wd4305 /wd4018 /wd4267 /wd4996 /wd4800 /wd4005 /wd4355 /wd4099 /wd4566
|
||||
/errorReport:prompt
|
||||
|
||||
:guilabel:`LIB_Release` Linker :guilabel:`Additional Dependencies`::
|
||||
|
||||
ws2_32.lib
|
||||
user32.lib
|
||||
zlib$(ZLIB_VERSION)-static-mtdll.lib
|
||||
libpng$(LIBPNG_VERSION)-static-mtdll.lib
|
||||
libjpeg$(LIBJPEG_VERSION)-static-mtdll.lib
|
||||
giflib$(GIFLIB_VERSION)-static-mtdll.lib
|
||||
libtiff$(LIBTIFF_VERSION)-static-mtdll.lib
|
||||
liblept$(LIBLEPT_VERSION)-static-mtdll.lib
|
||||
libtesseract$(LIBTESS_VERSION)-static.lib
|
||||
|
||||
:guilabel:`LIB_Debug` C/C++ :guilabel:`Command Line`::
|
||||
|
||||
/Od
|
||||
/I "." /I "..\..\include" /I "..\..\include\leptonica"
|
||||
/I "..\..\include\tesseract"
|
||||
/D "WIN32" /D "_WINDOWS" /D "_DEBUG"
|
||||
/D "USE_STD_NAMESPACE" /D "_MBCS"
|
||||
/FD /EHsc /RTC1 /MDd /Yc"stdafx.h"
|
||||
/Fp"LIB_Debug\baseapitesterd.pch" /Fo"LIB_Debug\\"
|
||||
/Fd"LIB_Debug\vc90.pdb"
|
||||
/W3 /nologo /c /Z7
|
||||
/wd4244 /wd4305 /wd4018 /wd4267 /wd4996 /wd4800 /wd4005 /wd4355 /wd4099 /wd4566
|
||||
/errorReport:prompt
|
||||
|
||||
:guilabel:`LIB_Debug` Linker :guilabel:`Additional Dependencies`::
|
||||
|
||||
ws2_32.lib
|
||||
user32.lib
|
||||
zlib$(ZLIB_VERSION)-static-mtdll-debug.lib
|
||||
libpng$(LIBPNG_VERSION)-static-mtdll-debug.lib
|
||||
libjpeg$(LIBJPEG_VERSION)-static-mtdll-debug.lib
|
||||
giflib$(GIFLIB_VERSION)-static-mtdll-debug.lib
|
||||
libtiff$(LIBTIFF_VERSION)-static-mtdll-debug.lib
|
||||
liblept$(LIBLEPT_VERSION)-static-mtdll-debug.lib
|
||||
libtesseract$(LIBTESS_VERSION)-static-debug.lib
|
||||
|
||||
:guilabel:`DLL_Release` C/C++ :guilabel:`Command Line`::
|
||||
|
||||
/O2
|
||||
/I "." /I "..\..\include" /I "..\..\include\leptonica"
|
||||
/I "..\..\include\tesseract"
|
||||
/D "WIN32" /D "_WINDOWS" /D "NDEBUG"
|
||||
/D "USE_STD_NAMESPACE" /D "_MBCS"
|
||||
/D "TESSDLL_IMPORTS" /D "CCUTIL_IMPORTS" /D "LIBLEPT_IMPORTS"
|
||||
/FD /EHsc /MD /Yc"stdafx.h"
|
||||
/Fp"DLL_Release\baseapitester-dll.pch" /Fo"DLL_Release\\"
|
||||
/Fd"DLL_Release\vc90.pdb"
|
||||
/W3 /nologo /c
|
||||
/wd4244 /wd4305 /wd4018 /wd4267 /wd4996 /wd4800 /wd4005 /wd4355 /wd4099 /wd4566
|
||||
/errorReport:prompt
|
||||
|
||||
:guilabel:`DLL_Release` Linker :guilabel:`Additional Dependencies`::
|
||||
|
||||
ws2_32.lib
|
||||
user32.lib
|
||||
liblept$(LIBLEPT_VERSION).lib
|
||||
libtesseract$(LIBTESS_VERSION).lib
|
||||
|
||||
:guilabel:`DLL_Debug` C/C++ :guilabel:`Command Line`::
|
||||
|
||||
/Od
|
||||
/I "." /I "..\..\include" /I "..\..\include\leptonica"
|
||||
/I "..\..\include\tesseract"
|
||||
/D "WIN32" /D "_WINDOWS" /D "_DEBUG"
|
||||
/D "USE_STD_NAMESPACE" /D "_MBCS"
|
||||
/D "TESSDLL_IMPORTS" /D "CCUTIL_IMPORTS" /D "LIBLEPT_IMPORTS"
|
||||
/FD /EHsc /RTC1 /MDd /Yc"stdafx.h"
|
||||
/Fp"DLL_Debug\baseapitester-dlld.pch" /Fo"DLL_Debug\\"
|
||||
/Fd"DLL_Debug\vc90.pdb"
|
||||
/W3 /nologo /c /Z7
|
||||
/wd4244 /wd4305 /wd4018 /wd4267 /wd4996 /wd4800 /wd4005 /wd4355 /wd4099 /wd4566
|
||||
/errorReport:prompt
|
||||
|
||||
:guilabel:`DLL_Debug` Linker :guilabel:`Additional Dependencies`::
|
||||
|
||||
ws2_32.lib
|
||||
user32.lib
|
||||
liblept$(LIBLEPT_VERSION)d.lib
|
||||
libtesseract$(LIBTESS_VERSION)d.lib
|
||||
|
||||
|
||||
.. _APIExamples:
|
||||
|
||||
APIExamples
|
||||
===========
|
||||
|
||||
<<<NEEDS WORK>>>
|
||||
|
||||
Currently two Projects are in this solution:
|
||||
|
||||
+ preprocessing -- Demonstrates how to use |Leptonica|\ ’s image
|
||||
processing functions to clean up images *before* calling
|
||||
``TessBaseAPI::SetImage()``.
|
||||
|
||||
+ getinfo -- Demonstrates calling various ``TessBaseAPI`` methods to get
|
||||
back information on the OCR process.
|
||||
|
||||
|
||||
|
||||
|Tesseractocr| preprocessor definitions
|
||||
=======================================
|
||||
|
||||
``HAVE_CONFIG_H``
|
||||
Only defined when building under Linux. This causes the inclusion of
|
||||
`config_auto.h`, which is only auto-generated during the `./configure`
|
||||
process and thus *not* visible on Windows.
|
||||
|
||||
This is what sets the ``VERSION`` macro (and lots of other
|
||||
configuration related macros).
|
||||
|
||||
|
||||
``TESSDLL_EXPORTS``
|
||||
Only used when *building* DLL versions of |libtess|.
|
||||
|
||||
``TESSDLL_IMPORTS``
|
||||
Should be defined when building apps that link to a DLL version of
|
||||
|libtess|. Used as follows in `baseapi.h`::
|
||||
|
||||
#ifdef TESSDLL_EXPORTS
|
||||
#define TESSDLL_API __declspec(dllexport)
|
||||
#elif defined(TESSDLL_IMPORTS)
|
||||
#define TESSDLL_API __declspec(dllimport)
|
||||
#else
|
||||
#define TESSDLL_API
|
||||
#endif
|
||||
|
||||
If you don't define this then you'll get "undefined external symbol"
|
||||
errors.
|
||||
|
||||
``TESSDLL_API``
|
||||
Used to mark classes for export (visibility) in DLL versions of
|
||||
|libtess|. Currently *only* used with the ``TestBaseAPI`` class.
|
||||
|
||||
|
||||
``CCUTIL_EXPORTS``
|
||||
Only used when *building* DLL versions of |libtess|.
|
||||
|
||||
``CCUTIL_IMPORTS``
|
||||
Should be defined when building apps that link to a DLL version of
|
||||
|libtess|. Used as follows in `strngs.h`::
|
||||
|
||||
#ifdef CCUTIL_EXPORTS
|
||||
#define CCUTIL_API __declspec(dllexport)
|
||||
#elif defined(CCUTIL_IMPORTS)
|
||||
#define CCUTIL_API __declspec(dllimport)
|
||||
#else
|
||||
#define CCUTIL_API
|
||||
#endif
|
||||
|
||||
If you don't define this then you'll get "undefined external symbol STRING"
|
||||
errors.
|
||||
|
||||
|
||||
``LIBLEPT_IMPORTS``
|
||||
Should be defined when building apps that link to a DLL version of
|
||||
|Leptonica|. Used as follows in environ.h::
|
||||
|
||||
#if defined(LIBLEPT_EXPORTS) || defined(LEPTONLIB_EXPORTS)
|
||||
#define LEPT_DLL __declspec(dllexport)
|
||||
#elif defined(LIBLEPT_IMPORTS) || defined(LEPTONLIB_IMPORTS)
|
||||
#define LEPT_DLL __declspec(dllimport)
|
||||
#else
|
||||
#define LEPT_DLL
|
||||
#endif
|
||||
|
||||
If you don't define this then you'll get "undefined external symbol"
|
||||
errors.
|
||||
|
||||
``USE_STD_NAMESPACE``
|
||||
Causes the following to be done::
|
||||
|
||||
#ifdef USE_STD_NAMESPACE
|
||||
using std::string;
|
||||
using std::vector;
|
||||
#endif
|
||||
|
||||
|
||||
``_WIN32``
|
||||
Used to indicate that the build target is Windows 32-bit or
|
||||
64-bit (``WIN32`` and ``WINDOWS`` are also added by the New Project
|
||||
Wizards).
|
||||
|
||||
See `C/C+ Preprocessor Reference | The Preprocessor | Macros |
|
||||
Predefined Macros
|
||||
<http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.90).aspx>`_ for
|
||||
the complete list for Visual Studio 2008.
|
||||
|
||||
``_MSC_VER``
|
||||
Used to check specifically for building with the VC++ compiler (as
|
||||
opposed to the MinGW gcc compiler).
|
||||
|
||||
``_USRDLL``
|
||||
Only defined when building the DLL versions of `libtesseract`.
|
||||
|
||||
``_MBCS``
|
||||
Automatically defined when :guilabel:`Configuration Properties |
|
||||
General | Character Set` is set to :guilabel:`Use Multi-Byte
|
||||
Character Set`.
|
||||
|
||||
|
||||
``DLLSYM``
|
||||
`Obsolete
|
||||
<http://groups.google.com/group/tesseract-dev/msg/5e0f7f7fab27b463>`_
|
||||
and can be ignored.
|
||||
|
||||
..
|
||||
Local Variables:
|
||||
coding: utf-8
|
||||
mode: rst
|
||||
indent-tabs-mode: nil
|
||||
sentence-end-double-space: t
|
||||
fill-column: 72
|
||||
mode: auto-fill
|
||||
standard-indent: 3
|
||||
tab-stop-list: (3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60)
|
||||
End:
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,744 +0,0 @@
|
|||
/******************************************************************************
|
||||
** 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"
|
||||
#include "helpers.h"
|
||||
#include "ccutil.h"
|
||||
#include "statistc.h"
|
||||
#include "trainingsample.h"
|
||||
#ifdef __UNIX__
|
||||
#endif
|
||||
|
||||
using tesseract::TrainingSample;
|
||||
|
||||
/**----------------------------------------------------------------------------
|
||||
Private Function Prototypes
|
||||
----------------------------------------------------------------------------**/
|
||||
int SaveFeature();
|
||||
uinT8 BinaryAnglePlusPi(inT32 Y, inT32 X);
|
||||
uinT8 MySqrt2();
|
||||
void ClipRadius();
|
||||
|
||||
INT_VAR(classify_radius_gyr_min_man, 255,
|
||||
"Minimum Radius of Gyration Mantissa 0-255: ");
|
||||
|
||||
INT_VAR(classify_radius_gyr_min_exp, 0,
|
||||
"Minimum Radius of Gyration Exponent 0-255: ");
|
||||
|
||||
INT_VAR(classify_radius_gyr_max_man, 158,
|
||||
"Maximum Radius of Gyration Mantissa 0-255: ");
|
||||
|
||||
INT_VAR(classify_radius_gyr_max_exp, 8,
|
||||
"Maximum Radius of Gyration Exponent 0-255: ");
|
||||
|
||||
/**----------------------------------------------------------------------------
|
||||
Global Data Definitions and Declarations
|
||||
----------------------------------------------------------------------------**/
|
||||
#define ATAN_TABLE_SIZE 64
|
||||
|
||||
// Look up table for arc tangent containing:
|
||||
// atan(0.0) ... atan(ATAN_TABLE_SIZE - 1 / ATAN_TABLE_SIZE)
|
||||
// The entries are in binary degrees where a full circle is 256 binary degrees.
|
||||
static uinT8 AtanTable[ATAN_TABLE_SIZE];
|
||||
// Look up table for cos and sin to turn the intfx feature angle to a vector.
|
||||
// Also protected by atan_table_mutex.
|
||||
static float cos_table[INT_CHAR_NORM_RANGE];
|
||||
static float sin_table[INT_CHAR_NORM_RANGE];
|
||||
// Guards write access to AtanTable so we dont create it more than once.
|
||||
tesseract::CCUtilMutex atan_table_mutex;
|
||||
|
||||
|
||||
/**----------------------------------------------------------------------------
|
||||
Public Code
|
||||
----------------------------------------------------------------------------**/
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void InitIntegerFX() {
|
||||
static bool atan_table_init = false;
|
||||
atan_table_mutex.Lock();
|
||||
if (!atan_table_init) {
|
||||
for (int i = 0; i < ATAN_TABLE_SIZE; i++) {
|
||||
AtanTable[i] =
|
||||
(uinT8) (atan ((i / (float) ATAN_TABLE_SIZE)) * 128.0 / PI + 0.5);
|
||||
}
|
||||
for (int i = 0; i < INT_CHAR_NORM_RANGE; ++i) {
|
||||
cos_table[i] = cos(i * 2 * PI / INT_CHAR_NORM_RANGE + PI);
|
||||
sin_table[i] = sin(i * 2 * PI / INT_CHAR_NORM_RANGE + PI);
|
||||
}
|
||||
atan_table_init = true;
|
||||
}
|
||||
atan_table_mutex.Unlock();
|
||||
}
|
||||
|
||||
// Returns a vector representing the direction of a feature with the given
|
||||
// theta direction in an INT_FEATURE_STRUCT.
|
||||
FCOORD FeatureDirection(uinT8 theta) {
|
||||
return FCOORD(cos_table[theta], sin_table[theta]);
|
||||
}
|
||||
|
||||
TrainingSample* GetIntFeatures(tesseract::NormalizationMode mode,
|
||||
TBLOB *blob, const DENORM& denorm) {
|
||||
INT_FEATURE_ARRAY blfeatures;
|
||||
INT_FEATURE_ARRAY cnfeatures;
|
||||
INT_FX_RESULT_STRUCT fx_info;
|
||||
ExtractIntFeat(blob, denorm, blfeatures, cnfeatures, &fx_info, NULL);
|
||||
TrainingSample* sample = NULL;
|
||||
if (mode == tesseract::NM_CHAR_ANISOTROPIC) {
|
||||
int num_features = fx_info.NumCN;
|
||||
if (num_features > 0) {
|
||||
sample = TrainingSample::CopyFromFeatures(fx_info, cnfeatures,
|
||||
num_features);
|
||||
}
|
||||
} else if (mode == tesseract::NM_BASELINE) {
|
||||
int num_features = fx_info.NumBL;
|
||||
if (num_features > 0) {
|
||||
sample = TrainingSample::CopyFromFeatures(fx_info, blfeatures,
|
||||
num_features);
|
||||
}
|
||||
} else {
|
||||
ASSERT_HOST(!"Unsupported normalization mode!");
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
// Extract a set of standard-sized features from Blobs and write them out in
|
||||
// two formats: baseline normalized and character normalized.
|
||||
//
|
||||
// We presume the Blobs are already scaled so that x-height=128 units
|
||||
//
|
||||
// Standard Features:
|
||||
// We take all outline segments longer than 7 units and chop them into
|
||||
// standard-sized segments of approximately 13 = (64 / 5) units.
|
||||
// When writing these features out, we output their center and angle as
|
||||
// measured counterclockwise from the vector <-1, 0>
|
||||
//
|
||||
// Baseline Normalized Output:
|
||||
// We center the grapheme by aligning the x-coordinate of its centroid with
|
||||
// x=0 and subtracting 128 from the y-coordinate.
|
||||
//
|
||||
// Character Normalized Output:
|
||||
// We align the grapheme's centroid at the origin and scale it asymmetrically
|
||||
// in x and y so that the result is vaguely square.
|
||||
//
|
||||
int ExtractIntFeat(TBLOB *Blob,
|
||||
const DENORM& denorm,
|
||||
INT_FEATURE_ARRAY BLFeat,
|
||||
INT_FEATURE_ARRAY CNFeat,
|
||||
INT_FX_RESULT_STRUCT* Results,
|
||||
inT32 *FeatureOutlineArray) {
|
||||
|
||||
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;
|
||||
// n - the number of features to extract from a given outline segment.
|
||||
// We extract features from every outline segment longer than ~6 units.
|
||||
// We chop these long segments into standard-sized features approximately
|
||||
// 13 (= 64 / 5) units in length.
|
||||
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;
|
||||
Results->YBottom = MAX_UINT8;
|
||||
Results->YTop = 0;
|
||||
|
||||
// Calculate the centroid (Xmean, Ymean) for the blob.
|
||||
// We use centroid (instead of center of bounding box or center of smallest
|
||||
// enclosing circle) so the algorithm will not be too greatly influenced by
|
||||
// small amounts of information at the edge of a character's bounding box.
|
||||
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 (!Segment->IsHidden()) {
|
||||
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 (Ix, Iy) & radius of gyration (Rx, Ry).
|
||||
//
|
||||
// Ix = Sum y^2 dA, where:
|
||||
// Ix: the second moment of area about the axis x
|
||||
// dA = 1 for our standard-sized piece of outline
|
||||
// y: the perependicular distance to the x axis
|
||||
// Rx = sqrt(Ix / A)
|
||||
// Note: 1 <= Rx <= height of blob / 2
|
||||
// Ry = sqrt(Iy / A)
|
||||
// Note: 1 <= Ry <= width of blob / 2
|
||||
Ix = 0;
|
||||
Iy = 0;
|
||||
NumBLFeatures = 0;
|
||||
OutLine = Blob->outlines;
|
||||
int min_x = 0;
|
||||
int max_x = 0;
|
||||
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;
|
||||
if (NormY < Results->YBottom)
|
||||
Results->YBottom = ClipToRange(NormY, 0, MAX_UINT8);
|
||||
if (NormY > Results->YTop)
|
||||
Results->YTop = ClipToRange(NormY, 0, MAX_UINT8);
|
||||
UpdateRange(NormX, &min_x, &max_x);
|
||||
|
||||
n = 1;
|
||||
if (!Segment->IsHidden()) {
|
||||
DeltaX = NormX - LastX;
|
||||
DeltaY = NormY - LastY;
|
||||
Length = MySqrt(DeltaX, DeltaY);
|
||||
n = ((Length << 2) + Length + 32) >> 6;
|
||||
if (n != 0) {
|
||||
Theta = BinaryAnglePlusPi(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);
|
||||
// TODO(eger): Hmmm... Xmean is not necessarily 0.
|
||||
// Figure out if we should center against Xmean for these
|
||||
// features, and if so fix Iy & SaveFeature().
|
||||
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;
|
||||
}
|
||||
Results->Width = max_x - min_x;
|
||||
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));
|
||||
if (Results->Ry == 0) {
|
||||
/*
|
||||
This would result in features having 'nan' values.
|
||||
Since the expression is always > 0, assign a value of 1.
|
||||
*/
|
||||
Results->Ry = 1;
|
||||
}
|
||||
Results->NumBL = NumBLFeatures;
|
||||
|
||||
// Extract character normalized features
|
||||
//
|
||||
// Rescale the co-ordinates to "equalize" distribution in X and Y, making
|
||||
// all of the following unichars be sized to look similar: , ' 1 i
|
||||
//
|
||||
// We calculate co-ordinates relative to the centroid, and then scale them
|
||||
// as follows (accomplishing a scale of up to 102.4 / dimension):
|
||||
// y *= 51.2 / Rx [ y scaled by 0.0 ... 102.4 / height of glyph ]
|
||||
// x *= 51.2 / Ry [ x scaled by 0.0 ... 102.4 / width of glyph ]
|
||||
// Although tempting to think so, this does not guarantee that our range
|
||||
// is within [-102.4...102.4] x [-102.4...102.4] because (Xmean, Ymean)
|
||||
// is the centroid, not the center of the bounding box. Instead, we can
|
||||
// only bound the result to [-204 ... 204] x [-204 ... 204]
|
||||
//
|
||||
NumCNFeatures = 0;
|
||||
OutLine = Blob->outlines;
|
||||
int OutLineIndex = -1;
|
||||
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;
|
||||
OutLineIndex++;
|
||||
|
||||
/* 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 (!Segment->IsHidden()) {
|
||||
DeltaX = NormX - LastX;
|
||||
DeltaY = NormY - LastY;
|
||||
Length = MySqrt(DeltaX, DeltaY);
|
||||
n = ((Length << 2) + Length + 32) >> 6;
|
||||
if (n != 0) {
|
||||
Theta = BinaryAnglePlusPi(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;
|
||||
if (FeatureOutlineArray) {
|
||||
FeatureOutlineArray[NumCNFeatures] = OutLineIndex;
|
||||
}
|
||||
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;
|
||||
if (FeatureOutlineArray) {
|
||||
FeatureOutlineArray[NumCNFeatures] = OutLineIndex;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
// Return the "binary angle" [0..255]
|
||||
// made by vector <X, Y> as measured counterclockwise from <-1, 0>
|
||||
// The order of the arguments follows the convention of atan2(3)
|
||||
uinT8 BinaryAnglePlusPi(inT32 Y, inT32 X) {
|
||||
inT16 Angle, Atan;
|
||||
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;
|
||||
Atan = AtanTable[Ratio];
|
||||
if (X >= 0)
|
||||
if (Y >= 0)
|
||||
if (AbsX > AbsY)
|
||||
Angle = Atan;
|
||||
else
|
||||
Angle = 64 - Atan;
|
||||
else if (AbsX > AbsY)
|
||||
Angle = 256 - Atan;
|
||||
else
|
||||
Angle = 192 + Atan;
|
||||
else if (Y >= 0)
|
||||
if (AbsX > AbsY)
|
||||
Angle = 128 - Atan;
|
||||
else
|
||||
Angle = 64 + Atan;
|
||||
else if (AbsX > AbsY)
|
||||
Angle = 128 + Atan;
|
||||
else
|
||||
Angle = 192 - Atan;
|
||||
|
||||
/* 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;
|
||||
|
||||
Feature->X = ClipToRange<inT16>(X, 0, 255);
|
||||
Feature->Y = ClipToRange<inT16>(Y, 0, 255);
|
||||
Feature->Theta = Theta;
|
||||
Feature->CP_misses = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
// Return floor(sqrt(min(emm, x)^2 + min(emm, y)^2))
|
||||
// where emm = EvidenceMultMask.
|
||||
uinT16 MySqrt(inT32 X, inT32 Y) {
|
||||
register uinT16 SqRoot;
|
||||
register uinT32 Square;
|
||||
register uinT16 BitLocation;
|
||||
register uinT32 Sum;
|
||||
const uinT32 EvidenceMultMask =
|
||||
((1 << IntegerMatcher::kIntEvidenceTruncBits) - 1);
|
||||
|
||||
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 = (EvidenceMultMask + 1) << 1;
|
||||
SqRoot = 0;
|
||||
do {
|
||||
Square = (SqRoot | BitLocation) * (SqRoot | BitLocation);
|
||||
if (Square <= Sum)
|
||||
SqRoot |= BitLocation;
|
||||
BitLocation >>= 1;
|
||||
}
|
||||
while (BitLocation);
|
||||
|
||||
return SqRoot;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
// Return two integers which can be used to express the sqrt(I/N):
|
||||
// sqrt(I/N) = 51.2 * 2^(*Exp) / retval
|
||||
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 = classify_radius_gyr_min_man;
|
||||
AE = classify_radius_gyr_min_exp;
|
||||
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 = classify_radius_gyr_min_man;
|
||||
*RxExp = classify_radius_gyr_min_exp;
|
||||
}
|
||||
|
||||
AM = classify_radius_gyr_min_man;
|
||||
AE = classify_radius_gyr_min_exp;
|
||||
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 = classify_radius_gyr_min_man;
|
||||
*RyExp = classify_radius_gyr_min_exp;
|
||||
}
|
||||
|
||||
AM = classify_radius_gyr_max_man;
|
||||
AE = classify_radius_gyr_max_exp;
|
||||
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 = classify_radius_gyr_max_man;
|
||||
BE = classify_radius_gyr_max_exp;
|
||||
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 = classify_radius_gyr_max_man;
|
||||
*RyExp = classify_radius_gyr_max_exp;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/* -*-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;
|
||||
|
||||
#endif
|
|
@ -1,36 +0,0 @@
|
|||
/* -*-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 <stdio.h>
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
F u n c t i o n s
|
||||
----------------------------------------------------------------------*/
|
||||
makestructure(new_cell, free_cell, list_rec);
|
|
@ -1,85 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#include "mfcpch.h"
|
||||
#include "fileerr.h"
|
||||
#ifdef __UNIX__
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include "basedir.h"
|
||||
#include "ccutil.h"
|
||||
|
||||
#define VARDIR "configs/" /*variables files */
|
||||
#define EXTERN
|
||||
|
||||
const ERRCODE NO_PATH =
|
||||
"Warning:explicit path for executable will not be used for configs";
|
||||
static const ERRCODE USAGE = "Usage";
|
||||
|
||||
namespace tesseract {
|
||||
/**********************************************************************
|
||||
* main_setup
|
||||
*
|
||||
* Main for mithras demo program. Read the arguments and set up globals.
|
||||
**********************************************************************/
|
||||
|
||||
void CCUtil::main_setup( /*main demo program */
|
||||
const char *argv0, //program name
|
||||
const char *basename //name of image
|
||||
) {
|
||||
imagebasename = basename; /*name of image */
|
||||
STRING dll_module_name;
|
||||
#ifdef _WIN32
|
||||
dll_module_name = tessedit_module_name;
|
||||
#endif
|
||||
|
||||
// TESSDATA_PREFIX Environment variable overrules everything.
|
||||
// Compiled in -DTESSDATA_PREFIX is next.
|
||||
// NULL goes to current directory.
|
||||
// An actual value of argv0 is used if getpath is successful.
|
||||
if (!getenv("TESSDATA_PREFIX")) {
|
||||
#ifdef TESSDATA_PREFIX
|
||||
#define _STR(a) #a
|
||||
#define _XSTR(a) _STR(a)
|
||||
datadir = _XSTR(TESSDATA_PREFIX);
|
||||
#undef _XSTR
|
||||
#undef _STR
|
||||
#else
|
||||
if (argv0 != NULL) {
|
||||
if (getpath(argv0, dll_module_name, datadir) < 0)
|
||||
#ifdef __UNIX__
|
||||
CANTOPENFILE.error("main", ABORT, "%s to get path", argv0);
|
||||
#else
|
||||
NO_PATH.error("main", DBG, NULL);
|
||||
#endif
|
||||
} else {
|
||||
datadir = "./";
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
datadir = getenv("TESSDATA_PREFIX");
|
||||
}
|
||||
|
||||
//datadir += m_data_sub_dir; /*data directory */
|
||||
}
|
||||
} // namespace tesseract
|
|
@ -1,8 +0,0 @@
|
|||
[theme]
|
||||
inherit = default
|
||||
stylesheet = tesseract.css
|
||||
pygments_style = sphinx
|
||||
|
||||
[options]
|
||||
collapsiblesidebar = true
|
||||
externalrefs = true
|
Двоичный файл не отображается.
|
@ -1,150 +0,0 @@
|
|||
/* -*-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 "ratngs.h"
|
||||
#include "params.h"
|
||||
#include "unicharset.h"
|
||||
|
||||
#define MAX_PERM_LENGTH 128
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
V a r i a b l e s
|
||||
----------------------------------------------------------------------*/
|
||||
extern INT_VAR_H(fragments_debug, 0, "Debug character fragments");
|
||||
extern INT_VAR_H(segment_debug, 0, "Debug the whole segmentation process");
|
||||
extern BOOL_VAR_H(permute_debug, 0, "char permutation debug");
|
||||
|
||||
extern BOOL_VAR_H(permute_script_word, 0,
|
||||
"Turn on word script consistency permuter");
|
||||
|
||||
extern BOOL_VAR_H(permute_fixed_length_dawg, 0,
|
||||
"Turn on fixed-length phrasebook search permuter");
|
||||
|
||||
extern BOOL_VAR_H(segment_segcost_rating, 0,
|
||||
"incorporate segmentation cost in word rating?");
|
||||
|
||||
extern double_VAR_H(segment_reward_script, 0.95,
|
||||
"Score multipler for script consistency within a word. "
|
||||
"Being a 'reward' factor, it should be <= 1. "
|
||||
"Smaller value implies bigger reward.");
|
||||
|
||||
extern BOOL_VAR_H(permute_chartype_word, 0,
|
||||
"Turn on character type (property) consistency permuter");
|
||||
extern double_VAR_H(segment_reward_chartype, 0.97,
|
||||
"Score multipler for char type consistency within a word. ");
|
||||
|
||||
extern double_VAR_H(segment_reward_ngram_best_choice, 0.99,
|
||||
"Score multipler for ngram permuter's best choice"
|
||||
" (only used in the Han script path).");
|
||||
|
||||
extern INT_VAR_H(max_permuter_attempts, 100000,
|
||||
"Maximum number of different character choices to consider"
|
||||
" during permutation. This limit is especially useful when"
|
||||
" user patterns are specified, since overly generic patterns"
|
||||
" can result in dawg search exploring an overly large number"
|
||||
"of options.");
|
||||
|
||||
extern int permute_only_top;
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
F u n c t i o n s
|
||||
----------------------------------------------------------------------*/
|
||||
void adjust_non_word(const char *word, const char *word_lengths,
|
||||
float rating, float *new_rating, float *adjust_factor);
|
||||
|
||||
const char* choose_il1(const char *first_char, //first choice
|
||||
const char *second_char, //second choice
|
||||
const char *third_char, //third choice
|
||||
const char *prev_char, //prev in word
|
||||
const char *next_char, //next in word
|
||||
const char *next_next_char);
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
// This is an awkward solution to allow "compounding" of permuter effects.
|
||||
// Right now, each permuter generates a WERD_CHOICE with some modified
|
||||
// rating which is compared to the current best choice, and the winner
|
||||
// is saved. Therefore, independent permuter improvements, eg. from script
|
||||
// consistency, dictionary check, and punctuation promoting, override each
|
||||
// other and can not be combined.
|
||||
// We need a trellis and someway to modify the path cost. Instead, we
|
||||
// approximate by saving a permutation string, which records the preferred
|
||||
// char choice [0-9] at each position [0..#chunks], and a cumulative reward
|
||||
// factor. Non-conflicting changes can be accumulated and the combined
|
||||
// result will be returned.
|
||||
// Default_bias is the initial value for the base multiplier. In other words,
|
||||
// it is the multiplier for raw choice rating if nothing is modified.
|
||||
// This would be 1.0 when used with reward-based permuters in CJK-path,
|
||||
// but it could be > 1 (eg. segment_penalty_garbage) to be compatible with
|
||||
// penalty-based permuters in the Latin path.
|
||||
// Note this class does not handle fragmented characters. It does so by
|
||||
// setting the preferred position of fragmented characters to '1' at Init,
|
||||
// which effectively skips the fragment choice. However, it can still be
|
||||
// overridden if collision is allowed. It is the responsibility of the
|
||||
// permuters to avoid permuting fragmented characters.
|
||||
class PermuterState {
|
||||
public:
|
||||
PermuterState();
|
||||
|
||||
void Init(const BLOB_CHOICE_LIST_VECTOR& char_choices,
|
||||
const UNICHARSET &unicharset,
|
||||
float default_bias,
|
||||
bool debug);
|
||||
|
||||
void AddPreference(int start_pos, char* pos_str, float weight);
|
||||
|
||||
void AddPreference(int char_pos, BLOB_CHOICE* blob_choice, float weight);
|
||||
|
||||
WERD_CHOICE* GetPermutedWord(float *certainties, float *adjust_factor);
|
||||
|
||||
void set_allow_collision(bool flag) { allow_collision_ = flag; }
|
||||
void set_adjust_factor(float factor) { adjust_factor_ = factor; }
|
||||
void set_debug(bool debug) { debug_ = debug; }
|
||||
bool position_marked(int pos) { return perm_state_[pos] != kPosFree; }
|
||||
|
||||
private:
|
||||
static const char kPosFree = '.';
|
||||
|
||||
const UNICHARSET *unicharset_;
|
||||
|
||||
const BLOB_CHOICE_LIST_VECTOR *char_choices_; // reference pointer only
|
||||
// does not need to be allocated or freed
|
||||
char perm_state_[MAX_PERM_LENGTH]; // handles upto MAX_PERM_LENGTH-1 states
|
||||
// stores preferred char choices, '0'..'9', or '.'
|
||||
int word_length_; // the number of char positions in the word
|
||||
bool allow_collision_; // can previously set preference to be overwritten?
|
||||
float adjust_factor_; // multiplying factor for rating adjustment
|
||||
bool debug_; // whether debug statements should be printed
|
||||
};
|
||||
|
||||
} // namespace tesseract
|
||||
|
||||
#endif
|
|
@ -1,499 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: devanagari_processing.cpp
|
||||
* Description: Methods to process images containing devanagari symbols,
|
||||
* prior to classification.
|
||||
* Author: Shobhit Saxena
|
||||
* Created: Mon Nov 17 20:26:01 IST 2008
|
||||
*
|
||||
* (C) Copyright 2008, 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 "devanagari_processing.h"
|
||||
#include "allheaders.h"
|
||||
#include "tordmain.h"
|
||||
#include "img.h"
|
||||
#include "statistc.h"
|
||||
|
||||
// Flags controlling the debugging information for shiro-rekha splitting
|
||||
// strategies.
|
||||
INT_VAR(devanagari_split_debuglevel, 0,
|
||||
"Debug level for split shiro-rekha process.");
|
||||
|
||||
BOOL_VAR(devanagari_split_debugimage, 0,
|
||||
"Whether to create a debug image for split shiro-rekha process.");
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
ShiroRekhaSplitter::ShiroRekhaSplitter() {
|
||||
orig_pix_ = NULL;
|
||||
segmentation_block_list_ = NULL;
|
||||
splitted_image_ = NULL;
|
||||
global_xheight_ = kUnspecifiedXheight;
|
||||
perform_close_ = false;
|
||||
debug_image_ = NULL;
|
||||
pageseg_split_strategy_ = NO_SPLIT;
|
||||
ocr_split_strategy_ = NO_SPLIT;
|
||||
}
|
||||
|
||||
ShiroRekhaSplitter::~ShiroRekhaSplitter() {
|
||||
Clear();
|
||||
}
|
||||
|
||||
void ShiroRekhaSplitter::Clear() {
|
||||
pixDestroy(&orig_pix_);
|
||||
pixDestroy(&splitted_image_);
|
||||
pageseg_split_strategy_ = NO_SPLIT;
|
||||
ocr_split_strategy_ = NO_SPLIT;
|
||||
pixDestroy(&debug_image_);
|
||||
segmentation_block_list_ = NULL;
|
||||
global_xheight_ = kUnspecifiedXheight;
|
||||
perform_close_ = false;
|
||||
}
|
||||
|
||||
// This method dumps a debug image to the specified location.
|
||||
void ShiroRekhaSplitter::DumpDebugImage(const char* filename) const {
|
||||
pixWrite(filename, debug_image_, IFF_PNG);
|
||||
}
|
||||
|
||||
// On setting the input image, a clone of it is owned by this class.
|
||||
void ShiroRekhaSplitter::set_orig_pix(Pix* pix) {
|
||||
if (orig_pix_) {
|
||||
pixDestroy(&orig_pix_);
|
||||
}
|
||||
orig_pix_ = pixClone(pix);
|
||||
}
|
||||
|
||||
// Top-level method to perform splitting based on current settings.
|
||||
// Returns true if a split was actually performed.
|
||||
// split_for_pageseg should be true if the splitting is being done prior to
|
||||
// page segmentation. This mode uses the flag
|
||||
// pageseg_devanagari_split_strategy to determine the splitting strategy.
|
||||
bool ShiroRekhaSplitter::Split(bool split_for_pageseg) {
|
||||
SplitStrategy split_strategy = split_for_pageseg ? pageseg_split_strategy_ :
|
||||
ocr_split_strategy_;
|
||||
if (split_strategy == NO_SPLIT) {
|
||||
return false; // Nothing to do.
|
||||
}
|
||||
ASSERT_HOST(split_strategy == MINIMAL_SPLIT ||
|
||||
split_strategy == MAXIMAL_SPLIT);
|
||||
ASSERT_HOST(orig_pix_);
|
||||
if (devanagari_split_debuglevel > 0) {
|
||||
tprintf("Splitting shiro-rekha ...\n");
|
||||
tprintf("Split strategy = %s\n",
|
||||
split_strategy == MINIMAL_SPLIT ? "Minimal" : "Maximal");
|
||||
tprintf("Initial pageseg available = %s\n",
|
||||
segmentation_block_list_ ? "yes" : "no");
|
||||
}
|
||||
// Create a copy of original image to store the splitting output.
|
||||
pixDestroy(&splitted_image_);
|
||||
splitted_image_ = pixCopy(NULL, orig_pix_);
|
||||
|
||||
// Initialize debug image if required.
|
||||
if (devanagari_split_debugimage) {
|
||||
pixDestroy(&debug_image_);
|
||||
debug_image_ = pixConvertTo32(orig_pix_);
|
||||
}
|
||||
|
||||
// Determine all connected components in the input image. A close operation
|
||||
// may be required prior to this, depending on the current settings.
|
||||
Pix* pix_for_ccs = pixClone(orig_pix_);
|
||||
if (perform_close_ && global_xheight_ != kUnspecifiedXheight &&
|
||||
!segmentation_block_list_) {
|
||||
if (devanagari_split_debuglevel > 0) {
|
||||
tprintf("Performing a global close operation..\n");
|
||||
}
|
||||
// A global measure is available for xheight, but no local information
|
||||
// exists.
|
||||
pixDestroy(&pix_for_ccs);
|
||||
pix_for_ccs = pixCopy(NULL, orig_pix_);
|
||||
PerformClose(pix_for_ccs, global_xheight_);
|
||||
}
|
||||
Pixa* ccs;
|
||||
Boxa* tmp_boxa = pixConnComp(pix_for_ccs, &ccs, 8);
|
||||
boxaDestroy(&tmp_boxa);
|
||||
pixDestroy(&pix_for_ccs);
|
||||
|
||||
// Iterate over all connected components. Get their bounding boxes and clip
|
||||
// out the image regions corresponding to these boxes from the original image.
|
||||
// Conditionally run splitting on each of them.
|
||||
Boxa* regions_to_clear = boxaCreate(0);
|
||||
for (int i = 0; i < pixaGetCount(ccs); ++i) {
|
||||
Box* box = ccs->boxa->box[i];
|
||||
Pix* word_pix = pixClipRectangle(orig_pix_, box, NULL);
|
||||
ASSERT_HOST(word_pix);
|
||||
int xheight = GetXheightForCC(box);
|
||||
if (xheight == kUnspecifiedXheight && segmentation_block_list_ &&
|
||||
devanagari_split_debugimage) {
|
||||
pixRenderBoxArb(debug_image_, box, 1, 255, 0, 0);
|
||||
}
|
||||
// If some xheight measure is available, attempt to pre-eliminate small
|
||||
// blobs from the shiro-rekha process. This is primarily to save the CCs
|
||||
// corresponding to punctuation marks/small dots etc which are part of
|
||||
// larger graphemes.
|
||||
if (xheight == kUnspecifiedXheight ||
|
||||
(box->w > xheight / 3 && box->h > xheight / 2)) {
|
||||
SplitWordShiroRekha(split_strategy, word_pix, xheight,
|
||||
box->x, box->y, regions_to_clear);
|
||||
} else if (devanagari_split_debuglevel > 0) {
|
||||
tprintf("CC dropped from splitting: %d,%d (%d, %d)\n",
|
||||
box->x, box->y, box->w, box->h);
|
||||
}
|
||||
pixDestroy(&word_pix);
|
||||
}
|
||||
// Actually clear the boxes now.
|
||||
for (int i = 0; i < boxaGetCount(regions_to_clear); ++i) {
|
||||
Box* box = boxaGetBox(regions_to_clear, i, L_CLONE);
|
||||
pixClearInRect(splitted_image_, box);
|
||||
boxDestroy(&box);
|
||||
}
|
||||
boxaDestroy(®ions_to_clear);
|
||||
pixaDestroy(&ccs);
|
||||
if (devanagari_split_debugimage) {
|
||||
DumpDebugImage(split_for_pageseg ? "pageseg_split_debug.png" :
|
||||
"ocr_split_debug.png");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method to perform a close operation on the input image. The xheight
|
||||
// estimate decides the size of sel used.
|
||||
void ShiroRekhaSplitter::PerformClose(Pix* pix, int xheight_estimate) {
|
||||
pixCloseBrick(pix, pix, xheight_estimate / 8, xheight_estimate / 3);
|
||||
}
|
||||
|
||||
// This method resolves the cc bbox to a particular row and returns the row's
|
||||
// xheight.
|
||||
int ShiroRekhaSplitter::GetXheightForCC(Box* cc_bbox) {
|
||||
if (!segmentation_block_list_) {
|
||||
return global_xheight_;
|
||||
}
|
||||
// Compute the box coordinates in Tesseract's coordinate system.
|
||||
TBOX bbox(cc_bbox->x,
|
||||
pixGetHeight(orig_pix_) - cc_bbox->y - cc_bbox->h - 1,
|
||||
cc_bbox->x + cc_bbox->w,
|
||||
pixGetHeight(orig_pix_) - cc_bbox->y - 1);
|
||||
// Iterate over all blocks.
|
||||
BLOCK_IT block_it(segmentation_block_list_);
|
||||
for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
|
||||
BLOCK* block = block_it.data();
|
||||
// Iterate over all rows in the block.
|
||||
ROW_IT row_it(block->row_list());
|
||||
for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
|
||||
ROW* row = row_it.data();
|
||||
if (!row->bounding_box().major_overlap(bbox)) {
|
||||
continue;
|
||||
}
|
||||
// Row could be skewed, warped, etc. Use the position of the box to
|
||||
// determine the baseline position of the row for that x-coordinate.
|
||||
// Create a square TBOX whose baseline's mid-point lies at this point
|
||||
// and side is row's xheight. Take the overlap of this box with the input
|
||||
// box and check if it is a 'major overlap'. If so, this box lies in this
|
||||
// row. In that case, return the xheight for this row.
|
||||
float box_middle = 0.5 * (bbox.left() + bbox.right());
|
||||
int baseline = static_cast<int>(row->base_line(box_middle) + 0.5);
|
||||
TBOX test_box(box_middle - row->x_height() / 2,
|
||||
baseline,
|
||||
box_middle + row->x_height() / 2,
|
||||
static_cast<int>(baseline + row->x_height()));
|
||||
// Compute overlap. If it is is a major overlap, this is the right row.
|
||||
if (bbox.major_overlap(test_box)) {
|
||||
return row->x_height();
|
||||
}
|
||||
}
|
||||
}
|
||||
// No row found for this bbox.
|
||||
return kUnspecifiedXheight;
|
||||
}
|
||||
|
||||
// Returns a list of regions (boxes) which should be cleared in the original
|
||||
// image so as to perform shiro-rekha splitting. Pix is assumed to carry one
|
||||
// (or less) word only. Xheight measure could be the global estimate, the row
|
||||
// estimate, or unspecified. If unspecified, over splitting may occur, since a
|
||||
// conservative estimate of stroke width along with an associated multiplier
|
||||
// is used in its place. It is advisable to have a specified xheight when
|
||||
// splitting for classification/training.
|
||||
// A vertical projection histogram of all the on-pixels in the input pix is
|
||||
// computed. The maxima of this histogram is regarded as an approximate location
|
||||
// of the shiro-rekha. By descending on the maxima's peak on both sides,
|
||||
// stroke width of shiro-rekha is estimated.
|
||||
// A horizontal projection histogram is computed for a sub-image of the input
|
||||
// image, which extends from just below the shiro-rekha down to a certain
|
||||
// leeway. The leeway depends on the input xheight, if provided, else a
|
||||
// conservative multiplier on approximate stroke width is used (which may lead
|
||||
// to over-splitting).
|
||||
void ShiroRekhaSplitter::SplitWordShiroRekha(SplitStrategy split_strategy,
|
||||
Pix* pix,
|
||||
int xheight,
|
||||
int word_left,
|
||||
int word_top,
|
||||
Boxa* regions_to_clear) {
|
||||
if (split_strategy == NO_SPLIT) {
|
||||
return;
|
||||
}
|
||||
int width = pixGetWidth(pix);
|
||||
int height = pixGetHeight(pix);
|
||||
// Statistically determine the yextents of the shiro-rekha.
|
||||
int shirorekha_top, shirorekha_bottom, shirorekha_ylevel;
|
||||
GetShiroRekhaYExtents(pix, &shirorekha_top, &shirorekha_bottom,
|
||||
&shirorekha_ylevel);
|
||||
// Since the shiro rekha is also a stroke, its width is equal to the stroke
|
||||
// width.
|
||||
int stroke_width = shirorekha_bottom - shirorekha_top + 1;
|
||||
|
||||
// Some safeguards to protect CCs we do not want to be split.
|
||||
// These are particularly useful when the word wasn't eliminated earlier
|
||||
// because xheight information was unavailable.
|
||||
if (shirorekha_ylevel > height / 2) {
|
||||
// Shirorekha shouldn't be in the bottom half of the word.
|
||||
if (devanagari_split_debuglevel > 0) {
|
||||
tprintf("Skipping splitting CC at (%d, %d): shirorekha in lower half..\n",
|
||||
word_left, word_top);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (stroke_width > height / 3) {
|
||||
// Even the boldest of fonts shouldn't do this.
|
||||
if (devanagari_split_debuglevel > 0) {
|
||||
tprintf("Skipping splitting CC at (%d, %d): stroke width too huge..\n",
|
||||
word_left, word_top);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the ascender and descender regions of the word.
|
||||
// Obtain a vertical projection histogram for the resulting image.
|
||||
Box* box_to_clear = boxCreate(0, shirorekha_top - stroke_width / 3,
|
||||
width, 5 * stroke_width / 3);
|
||||
Pix* word_in_xheight = pixCopy(NULL, pix);
|
||||
pixClearInRect(word_in_xheight, box_to_clear);
|
||||
// Also clear any pixels which are below shirorekha_bottom + some leeway.
|
||||
// The leeway is set to xheight if the information is available, else it is a
|
||||
// multiplier applied to the stroke width.
|
||||
int leeway_to_keep = stroke_width * 3;
|
||||
if (xheight != kUnspecifiedXheight) {
|
||||
// This is because the xheight-region typically includes the shiro-rekha
|
||||
// inside it, i.e., the top of the xheight range corresponds to the top of
|
||||
// shiro-rekha.
|
||||
leeway_to_keep = xheight - stroke_width;
|
||||
}
|
||||
box_to_clear->y = shirorekha_bottom + leeway_to_keep;
|
||||
box_to_clear->h = height - box_to_clear->y;
|
||||
pixClearInRect(word_in_xheight, box_to_clear);
|
||||
boxDestroy(&box_to_clear);
|
||||
|
||||
PixelHistogram vert_hist;
|
||||
vert_hist.ConstructVerticalCountHist(word_in_xheight);
|
||||
pixDestroy(&word_in_xheight);
|
||||
|
||||
// If the number of black pixel in any column of the image is less than a
|
||||
// fraction of the stroke width, treat it as noise / a stray mark. Perform
|
||||
// these changes inside the vert_hist data itself, as that is used later on as
|
||||
// a bit vector for the final split decision at every column.
|
||||
for (int i = 0; i < width; ++i) {
|
||||
if (vert_hist.hist()[i] <= stroke_width / 4)
|
||||
vert_hist.hist()[i] = 0;
|
||||
else
|
||||
vert_hist.hist()[i] = 1;
|
||||
}
|
||||
// In order to split the line at any point, we make sure that the width of the
|
||||
// gap is atleast half the stroke width.
|
||||
int i = 0;
|
||||
int cur_component_width = 0;
|
||||
while (i < width) {
|
||||
if (!vert_hist.hist()[i]) {
|
||||
int j = 0;
|
||||
while (i + j < width && !vert_hist.hist()[i+j])
|
||||
++j;
|
||||
if (j >= stroke_width / 2 && cur_component_width >= stroke_width / 2) {
|
||||
// Perform a shiro-rekha split. The intervening region lies from i to
|
||||
// i+j-1.
|
||||
// A minimal single-pixel split makes the estimation of intra- and
|
||||
// inter-word spacing easier during page layout analysis,
|
||||
// whereas a maximal split may be needed for OCR, depending on
|
||||
// how the engine was trained.
|
||||
bool minimal_split = (split_strategy == MINIMAL_SPLIT);
|
||||
int split_width = minimal_split ? 1 : j;
|
||||
int split_left = minimal_split ? i + (j / 2) - (split_width / 2) : i;
|
||||
if (!minimal_split || (i != 0 && i + j != width)) {
|
||||
Box* box_to_clear =
|
||||
boxCreate(word_left + split_left,
|
||||
word_top + shirorekha_top - stroke_width / 3,
|
||||
split_width,
|
||||
5 * stroke_width / 3);
|
||||
if (box_to_clear) {
|
||||
boxaAddBox(regions_to_clear, box_to_clear, L_CLONE);
|
||||
// Mark this in the debug image if needed.
|
||||
if (devanagari_split_debugimage) {
|
||||
pixRenderBoxArb(debug_image_, box_to_clear, 1, 128, 255, 128);
|
||||
}
|
||||
boxDestroy(&box_to_clear);
|
||||
cur_component_width = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += j;
|
||||
} else {
|
||||
++i;
|
||||
++cur_component_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refreshes the words in the segmentation block list by using blobs in the
|
||||
// input block list.
|
||||
// The segmentation block list must be set.
|
||||
void ShiroRekhaSplitter::RefreshSegmentationWithNewBlobs(
|
||||
C_BLOB_LIST* new_blobs) {
|
||||
// The segmentation block list must have been specified.
|
||||
ASSERT_HOST(segmentation_block_list_);
|
||||
if (devanagari_split_debuglevel > 0) {
|
||||
tprintf("Before refreshing blobs:\n");
|
||||
PrintSegmentationStats(segmentation_block_list_);
|
||||
tprintf("New Blobs found: %d\n", new_blobs->length());
|
||||
}
|
||||
|
||||
C_BLOB_LIST not_found_blobs;
|
||||
RefreshWordBlobsFromNewBlobs(segmentation_block_list_,
|
||||
new_blobs,
|
||||
((devanagari_split_debugimage && debug_image_) ?
|
||||
¬_found_blobs : NULL));
|
||||
|
||||
if (devanagari_split_debuglevel > 0) {
|
||||
tprintf("After refreshing blobs:\n");
|
||||
PrintSegmentationStats(segmentation_block_list_);
|
||||
}
|
||||
if (devanagari_split_debugimage && debug_image_) {
|
||||
// Plot out the original blobs for which no match was found in the new
|
||||
// all_blobs list.
|
||||
C_BLOB_IT not_found_it(¬_found_blobs);
|
||||
for (not_found_it.mark_cycle_pt(); !not_found_it.cycled_list();
|
||||
not_found_it.forward()) {
|
||||
C_BLOB* not_found = not_found_it.data();
|
||||
TBOX not_found_box = not_found->bounding_box();
|
||||
Box* box_to_plot = GetBoxForTBOX(not_found_box);
|
||||
pixRenderBoxArb(debug_image_, box_to_plot, 1, 255, 0, 255);
|
||||
boxDestroy(&box_to_plot);
|
||||
}
|
||||
|
||||
// Plot out the blobs unused from all blobs.
|
||||
C_BLOB_IT all_blobs_it(new_blobs);
|
||||
for (all_blobs_it.mark_cycle_pt(); !all_blobs_it.cycled_list();
|
||||
all_blobs_it.forward()) {
|
||||
C_BLOB* a_blob = all_blobs_it.data();
|
||||
Box* box_to_plot = GetBoxForTBOX(a_blob->bounding_box());
|
||||
pixRenderBoxArb(debug_image_, box_to_plot, 3, 0, 127, 0);
|
||||
boxDestroy(&box_to_plot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a new box object for the corresponding TBOX, based on the original
|
||||
// image's coordinate system.
|
||||
Box* ShiroRekhaSplitter::GetBoxForTBOX(const TBOX& tbox) const {
|
||||
return boxCreate(tbox.left(), pixGetHeight(orig_pix_) - tbox.top() - 1,
|
||||
tbox.width(), tbox.height());
|
||||
}
|
||||
|
||||
// This method returns the computed mode-height of blobs in the pix.
|
||||
// It also prunes very small blobs from calculation.
|
||||
int ShiroRekhaSplitter::GetModeHeight(Pix* pix) {
|
||||
Boxa* boxa = pixConnComp(pix, NULL, 8);
|
||||
STATS heights(0, pixGetHeight(pix));
|
||||
heights.clear();
|
||||
for (int i = 0; i < boxaGetCount(boxa); ++i) {
|
||||
Box* box = boxaGetBox(boxa, i, L_CLONE);
|
||||
if (box->h >= 3 || box->w >= 3) {
|
||||
heights.add(box->h, 1);
|
||||
}
|
||||
boxDestroy(&box);
|
||||
}
|
||||
boxaDestroy(&boxa);
|
||||
return heights.mode();
|
||||
}
|
||||
|
||||
// This method returns y-extents of the shiro-rekha computed from the input
|
||||
// word image.
|
||||
void ShiroRekhaSplitter::GetShiroRekhaYExtents(Pix* word_pix,
|
||||
int* shirorekha_top,
|
||||
int* shirorekha_bottom,
|
||||
int* shirorekha_ylevel) {
|
||||
// Compute a histogram from projecting the word on a vertical line.
|
||||
PixelHistogram hist_horiz;
|
||||
hist_horiz.ConstructHorizontalCountHist(word_pix);
|
||||
// Get the ylevel where the top-line exists. This is basically the global
|
||||
// maxima in the horizontal histogram.
|
||||
int topline_onpixel_count = 0;
|
||||
int topline_ylevel = hist_horiz.GetHistogramMaximum(&topline_onpixel_count);
|
||||
|
||||
// Get the upper and lower extents of the shiro rekha.
|
||||
int thresh = (topline_onpixel_count * 70) / 100;
|
||||
int ulimit = topline_ylevel;
|
||||
int llimit = topline_ylevel;
|
||||
while (ulimit > 0 && hist_horiz.hist()[ulimit] >= thresh)
|
||||
--ulimit;
|
||||
while (llimit < word_pix->h && hist_horiz.hist()[llimit] >= thresh)
|
||||
++llimit;
|
||||
|
||||
if (shirorekha_top) *shirorekha_top = ulimit;
|
||||
if (shirorekha_bottom) *shirorekha_bottom = llimit;
|
||||
if (shirorekha_ylevel) *shirorekha_ylevel = topline_ylevel;
|
||||
}
|
||||
|
||||
// This method returns the global-maxima for the histogram. The frequency of
|
||||
// the global maxima is returned in count, if specified.
|
||||
int PixelHistogram::GetHistogramMaximum(int* count) const {
|
||||
int best_value = 0;
|
||||
for (int i = 0; i < length_; ++i) {
|
||||
if (hist_[i] > hist_[best_value]) {
|
||||
best_value = i;
|
||||
}
|
||||
}
|
||||
if (count) {
|
||||
*count = hist_[best_value];
|
||||
}
|
||||
return best_value;
|
||||
}
|
||||
|
||||
// Methods to construct histograms from images.
|
||||
void PixelHistogram::ConstructVerticalCountHist(Pix* pix) {
|
||||
Clear();
|
||||
int width = pixGetWidth(pix);
|
||||
int height = pixGetHeight(pix);
|
||||
hist_ = new int[width];
|
||||
length_ = width;
|
||||
int wpl = pixGetWpl(pix);
|
||||
l_uint32 *data = pixGetData(pix);
|
||||
for (int i = 0; i < width; ++i)
|
||||
hist_[i] = 0;
|
||||
for (int i = 0; i < height; ++i) {
|
||||
l_uint32 *line = data + i * wpl;
|
||||
for (int j = 0; j < width; ++j)
|
||||
if (GET_DATA_BIT(line, j))
|
||||
++(hist_[j]);
|
||||
}
|
||||
}
|
||||
|
||||
void PixelHistogram::ConstructHorizontalCountHist(Pix* pix) {
|
||||
Clear();
|
||||
Numa* counts = pixCountPixelsByRow(pix, NULL);
|
||||
length_ = numaGetCount(counts);
|
||||
hist_ = new int[length_];
|
||||
for (int i = 0; i < length_; ++i) {
|
||||
l_int32 val = 0;
|
||||
numaGetIValue(counts, i, &val);
|
||||
hist_[i] = val;
|
||||
}
|
||||
numaDestroy(&counts);
|
||||
}
|
||||
|
||||
} // namespace tesseract.
|
|
@ -1,190 +0,0 @@
|
|||
/******************************************************************************
|
||||
** 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 "classify.h"
|
||||
#include "efio.h"
|
||||
#include "featdefs.h"
|
||||
#include "mfoutline.h"
|
||||
#include "ocrfeatures.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/**----------------------------------------------------------------------------
|
||||
Public Code
|
||||
----------------------------------------------------------------------------**/
|
||||
/*---------------------------------------------------------------------------*/
|
||||
namespace tesseract {
|
||||
FEATURE_SET Classify::ExtractOutlineFeatures(TBLOB *Blob) {
|
||||
/*
|
||||
** 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, &XScale, &YScale);
|
||||
RemainingOutlines = Outlines;
|
||||
iterate(RemainingOutlines) {
|
||||
Outline = (MFOUTLINE) first_node (RemainingOutlines);
|
||||
ConvertToOutlineFeatures(Outline, FeatureSet);
|
||||
}
|
||||
if (classify_norm_method == baseline)
|
||||
NormalizeOutlineX(FeatureSet);
|
||||
FreeOutlines(Outlines);
|
||||
return (FeatureSet);
|
||||
} /* ExtractOutlineFeatures */
|
||||
} // namespace tesseract
|
||||
|
||||
/**----------------------------------------------------------------------------
|
||||
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);
|
||||
Feature->Params[OutlineFeatDir] = NormalizedAngleFrom(Start, End, 1.0);
|
||||
Feature->Params[OutlineFeatX] = AverageOf(Start->x, End->x);
|
||||
Feature->Params[OutlineFeatY] = AverageOf(Start->y, End->y);
|
||||
Feature->Params[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 {
|
||||
FeatureStart = PointAt(Next)->Point;
|
||||
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 (!PointAt(Next)->Hidden) {
|
||||
FeatureEnd = PointAt(Next)->Point;
|
||||
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 (FeatureSet->NumFeatures <= 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < FeatureSet->NumFeatures; i++) {
|
||||
Feature = FeatureSet->Features[i];
|
||||
Length = Feature->Params[OutlineFeatLength];
|
||||
TotalX += Feature->Params[OutlineFeatX] * Length;
|
||||
TotalWeight += Length;
|
||||
}
|
||||
Origin = TotalX / TotalWeight;
|
||||
|
||||
for (i = 0; i < FeatureSet->NumFeatures; i++) {
|
||||
Feature = FeatureSet->Features[i];
|
||||
Feature->Params[OutlineFeatX] -= Origin;
|
||||
}
|
||||
} /* NormalizeOutlineX */
|
|
@ -1,134 +0,0 @@
|
|||
/* -*-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 <math.h>
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
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) < chop_same_distance) && \
|
||||
(abs (p1.y - p2.y) < chop_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)))
|
||||
|
||||
#endif
|
|
@ -1,8 +0,0 @@
|
|||
Ray Smith (lead developer) <theraysmith@gmail.com>
|
||||
Phil Cheatle
|
||||
Simon Crouch
|
||||
Dan Johnson
|
||||
Mark Seaman
|
||||
Sheelagh Huddleston
|
||||
Chris Newton
|
||||
... and several others.
|
|
@ -1,42 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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 "ratngs.h"
|
||||
#include "blobs.h"
|
||||
#include "notdll.h"
|
||||
#include "tesseractclass.h"
|
||||
|
||||
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
|
|
@ -1,116 +0,0 @@
|
|||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
// Author: rays@google.com (Ray Smith)
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// File: bitvector.h
|
||||
// Description: Class replacement for BITVECTOR.
|
||||
// Author: Ray Smith
|
||||
// Created: Mon Jan 10 17:44:01 PST 2011
|
||||
//
|
||||
// (C) Copyright 2011, 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 TESSERACT_CCUTIL_BITVECTOR_H__
|
||||
#define TESSERACT_CCUTIL_BITVECTOR_H__
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include "host.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
// Trivial class to encapsulate a fixed-length array of bits, with
|
||||
// Serialize/DeSerialize. Replaces the old macros.
|
||||
class BitVector {
|
||||
public:
|
||||
BitVector();
|
||||
// Initializes the array to length * false.
|
||||
explicit BitVector(int length);
|
||||
BitVector(const BitVector& src);
|
||||
BitVector& operator=(const BitVector& src);
|
||||
~BitVector();
|
||||
|
||||
// Initializes the array to length * false.
|
||||
void Init(int length);
|
||||
|
||||
// Returns the number of bits that are accessible in the vector.
|
||||
int size() const {
|
||||
return bit_size_;
|
||||
}
|
||||
|
||||
// Writes to the given file. Returns false in case of error.
|
||||
bool Serialize(FILE* fp) const;
|
||||
// Reads from the given file. Returns false in case of error.
|
||||
// If swap is true, assumes a big/little-endian swap is needed.
|
||||
bool DeSerialize(bool swap, FILE* fp);
|
||||
|
||||
void SetAllFalse();
|
||||
void SetAllTrue();
|
||||
|
||||
// Accessors to set/reset/get bits.
|
||||
// The range of index is [0, size()-1].
|
||||
// There is debug-only bounds checking.
|
||||
void SetBit(int index) {
|
||||
array_[WordIndex(index)] |= BitMask(index);
|
||||
}
|
||||
void ResetBit(int index) {
|
||||
array_[WordIndex(index)] &= ~BitMask(index);
|
||||
}
|
||||
void SetValue(int index, bool value) {
|
||||
if (value)
|
||||
SetBit(index);
|
||||
else
|
||||
ResetBit(index);
|
||||
}
|
||||
bool At(int index) const {
|
||||
return (array_[WordIndex(index)] & BitMask(index)) != 0;
|
||||
}
|
||||
bool operator[](int index) const {
|
||||
return (array_[WordIndex(index)] & BitMask(index)) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
// Allocates memory for a vector of the given length.
|
||||
void Alloc(int length);
|
||||
|
||||
// Computes the index to array_ for the given index, with debug range
|
||||
// checking.
|
||||
int WordIndex(int index) const {
|
||||
assert(0 <= index && index < bit_size_);
|
||||
return index / kBitFactor;
|
||||
}
|
||||
// Returns a mask to select the appropriate bit for the given index.
|
||||
uinT32 BitMask(int index) const {
|
||||
return 1 << (index & (kBitFactor - 1));
|
||||
}
|
||||
// Returns the number of array elements needed to represent the current
|
||||
// bit_size_.
|
||||
int WordLength() const {
|
||||
return (bit_size_ + kBitFactor - 1) / kBitFactor;
|
||||
}
|
||||
// Returns the number of bytes consumed by the array_.
|
||||
int ByteLength() const {
|
||||
return WordLength() * sizeof(*array_);
|
||||
}
|
||||
|
||||
// Number of bits in this BitVector.
|
||||
uinT32 bit_size_;
|
||||
// Array of words used to pack the bits.
|
||||
uinT32* array_;
|
||||
// Number of bits in an array_ element.
|
||||
static const int kBitFactor = sizeof(uinT32) * 8;
|
||||
};
|
||||
|
||||
} // namespace tesseract.
|
||||
|
||||
#endif // TESSERACT_CCUTIL_BITVECTOR_H__
|
|
@ -1,156 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: cube_page_segmenter.h
|
||||
* Description: Declaration of the Cube Page Segmenter Class
|
||||
* Author: Ahmad Abdulkader
|
||||
* Created: 2007
|
||||
*
|
||||
* (C) Copyright 2008, 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.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
// TODO(ahmadab)
|
||||
// This is really a makeshift line segmenter that works well for Arabic
|
||||
// This should eventually be replaced by Ray Smith's Page segmenter
|
||||
// There are lots of magic numbers below that were determined empirically
|
||||
// but not thoroughly tested
|
||||
|
||||
#ifndef CUBE_LINE_SEGMENTER_H
|
||||
#define CUBE_LINE_SEGMENTER_H
|
||||
|
||||
#include "cube_reco_context.h"
|
||||
#include "allheaders.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
class CubeLineSegmenter {
|
||||
public:
|
||||
CubeLineSegmenter(CubeRecoContext *cntxt, Pix *img);
|
||||
~CubeLineSegmenter();
|
||||
|
||||
// Accessor functions
|
||||
Pix *PostProcessedImage() {
|
||||
if (init_ == false && Init() == false) {
|
||||
return NULL;
|
||||
}
|
||||
return img_;
|
||||
}
|
||||
int ColumnCnt() {
|
||||
if (init_ == false && Init() == false) {
|
||||
return NULL;
|
||||
}
|
||||
return columns_->n;
|
||||
}
|
||||
Box *Column(int col) {
|
||||
if (init_ == false && Init() == false) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return columns_->boxa->box[col];
|
||||
}
|
||||
int LineCnt() {
|
||||
if (init_ == false && Init() == false) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return line_cnt_;
|
||||
}
|
||||
Pixa *ConComps() {
|
||||
if (init_ == false && Init() == false) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return con_comps_;
|
||||
}
|
||||
Pixaa *Columns() {
|
||||
if (init_ == false && Init() == false) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return columns_;
|
||||
}
|
||||
inline double AlefHgtEst() { return est_alef_hgt_; }
|
||||
inline double DotHgtEst() { return est_dot_hgt_; }
|
||||
Pix *Line(int line, Box **line_box);
|
||||
|
||||
private:
|
||||
static const float kMinValidLineHgtRatio;
|
||||
static const int kLineSepMorphMinHgt;
|
||||
static const int kHgtBins;
|
||||
static const int kMaxConnCompHgt;
|
||||
static const int kMaxConnCompWid;
|
||||
static const int kMaxHorzAspectRatio;
|
||||
static const int kMaxVertAspectRatio;
|
||||
static const int kMinWid;
|
||||
static const int kMinHgt;
|
||||
static const double kMaxValidLineRatio;
|
||||
|
||||
// Cube Reco context
|
||||
CubeRecoContext *cntxt_;
|
||||
// Original image
|
||||
Pix *orig_img_;
|
||||
// Post processed image
|
||||
Pix *img_;
|
||||
// Init flag
|
||||
bool init_;
|
||||
// Output Line and column info
|
||||
int line_cnt_;
|
||||
Pixaa *columns_;
|
||||
Pixa *con_comps_;
|
||||
Pixa *lines_pixa_;
|
||||
// Estimates for sizes of ALEF and DOT needed for Arabic analysis
|
||||
double est_alef_hgt_;
|
||||
double est_dot_hgt_;
|
||||
|
||||
// Init the page analysis
|
||||
bool Init();
|
||||
// Performs line segmentation
|
||||
bool LineSegment();
|
||||
// Cleanup function
|
||||
Pix *CleanUp(Pix *pix);
|
||||
// compute validity ratio for a line
|
||||
double ValidityRatio(Pix *line_mask_pix, Box *line_box);
|
||||
// validate line
|
||||
bool ValidLine(Pix *line_mask_pix, Box *line_box);
|
||||
// split a line continuously until valid or fail
|
||||
Pixa *SplitLine(Pix *line_mask_pix, Box *line_box);
|
||||
// do a desperate attempt at cracking lines
|
||||
Pixa *CrackLine(Pix *line_mask_pix, Box *line_box);
|
||||
Pixa *CrackLine(Pix *line_mask_pix, Box *line_box, int line_cnt);
|
||||
// Checks of a line is too small
|
||||
bool SmallLine(Box *line_box);
|
||||
// Compute the connected components in a line
|
||||
Boxa * ComputeLineConComps(Pix *line_mask_pix, Box *line_box,
|
||||
Pixa **con_comps_pixa);
|
||||
// create a union of two arbitrary pix
|
||||
Pix *PixUnion(Pix *dest_pix, Box *dest_box, Pix *src_pix, Box *src_box);
|
||||
// create a union of a pixa subset
|
||||
Pix *Pixa2Pix(Pixa *pixa, Box **dest_box, int start_pix, int pix_cnt);
|
||||
// create a union of a pixa
|
||||
Pix *Pixa2Pix(Pixa *pixa, Box **dest_box);
|
||||
// merges a number of lines into one line given a bounding box and a mask
|
||||
bool MergeLine(Pix *line_mask_pix, Box *line_box,
|
||||
Pixa *lines, Boxaa *lines_con_comps);
|
||||
// Creates new set of lines from the computed columns
|
||||
bool AddLines(Pixa *lines);
|
||||
// Estimate the parameters of the font(s) used in the page
|
||||
bool EstimateFontParams();
|
||||
// perform a vertical Closing with the specified threshold
|
||||
// returning the resulting conn comps as a pixa
|
||||
Pixa *VerticalClosing(Pix *pix, int thresold, Boxa **boxa);
|
||||
// Index the specific pixa using RTL reading order
|
||||
int *IndexRTL(Pixa *pixa);
|
||||
// Implements a rudimentary page & line segmenter
|
||||
bool FindLines();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CUBE_LINE_SEGMENTER_H
|
|
@ -1,142 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////
|
||||
// File: svutil.h
|
||||
// Description: ScrollView Utilities
|
||||
// Author: Joern Wanke
|
||||
// Created: Thu Nov 29 2007
|
||||
//
|
||||
// (C) Copyright 2007, 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.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SVUtil contains the SVSync, SVSemaphore, SVMutex and SVNetwork
|
||||
// classes, which are used for thread/process creation & synchronization
|
||||
// and network connection.
|
||||
|
||||
#ifndef TESSERACT_VIEWER_SVUTIL_H__
|
||||
#define TESSERACT_VIEWER_SVUTIL_H__
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef __GNUC__
|
||||
#include <windows.h>
|
||||
#define snprintf _snprintf
|
||||
#if (_MSC_VER <= 1400)
|
||||
#define vsnprintf _vsnprintf
|
||||
#endif
|
||||
#pragma warning(disable:4786)
|
||||
#else
|
||||
#include "platform.h"
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a > b) ? a : b)
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a < b) ? a : b)
|
||||
#endif
|
||||
|
||||
/// The SVSync class provides functionality for Thread & Process Creation
|
||||
class SVSync {
|
||||
public:
|
||||
/// Create new thread.
|
||||
static void StartThread(void *(*func)(void*), void* arg);
|
||||
/// Signals a thread to exit.
|
||||
static void ExitThread();
|
||||
/// Starts a new process.
|
||||
static void StartProcess(const char* executable, const char* args);
|
||||
};
|
||||
|
||||
/// A semaphore class which encapsulates the main signalling
|
||||
/// and wait abilities of semaphores for windows and unix.
|
||||
class SVSemaphore {
|
||||
public:
|
||||
/// Sets up a semaphore.
|
||||
SVSemaphore();
|
||||
/// Signal a semaphore.
|
||||
void Signal();
|
||||
/// Wait on a semaphore.
|
||||
void Wait();
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
HANDLE semaphore_;
|
||||
#else
|
||||
sem_t semaphore_;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// A mutex which encapsulates the main locking and unlocking
|
||||
/// abilites of mutexes for windows and unix.
|
||||
class SVMutex {
|
||||
public:
|
||||
/// Sets up a new mutex.
|
||||
SVMutex();
|
||||
/// Locks on a mutex.
|
||||
void Lock();
|
||||
/// Unlocks on a mutex.
|
||||
void Unlock();
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
HANDLE mutex_;
|
||||
#else
|
||||
pthread_mutex_t mutex_;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// The SVNetwork class takes care of the remote connection for ScrollView
|
||||
/// This means setting up and maintaining a remote connection, sending and
|
||||
/// receiving messages and closing the connection.
|
||||
/// It is designed to work on both Linux and Windows.
|
||||
class SVNetwork {
|
||||
public:
|
||||
/// Set up a connection to hostname on port.
|
||||
SVNetwork(const char* hostname, int port);
|
||||
|
||||
/// Destructor.
|
||||
~SVNetwork();
|
||||
|
||||
/// Put a message in the messagebuffer to the server and try to send it.
|
||||
void Send(const char* msg);
|
||||
|
||||
/// Receive a message from the server.
|
||||
/// This will always return one line of char* (denoted by \n).
|
||||
char* Receive();
|
||||
|
||||
/// Close the connection to the server.
|
||||
void Close();
|
||||
|
||||
/// Flush the buffer.
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
/// The mutex for access to Send() and Flush().
|
||||
SVMutex* mutex_send_;
|
||||
/// The actual stream_ to the server.
|
||||
int stream_;
|
||||
/// Stores the last received message-chunk from the server.
|
||||
char* msg_buffer_in_;
|
||||
|
||||
/// Stores the messages which are supposed to go out.
|
||||
std::string msg_buffer_out_;
|
||||
|
||||
bool has_content; // Win32 (strtok)
|
||||
/// Where we are at in our msg_buffer_in_
|
||||
char* buffer_ptr_; // Unix (strtok_r)
|
||||
};
|
||||
|
||||
#endif // TESSERACT_VIEWER_SVUTIL_H__
|
|
@ -1,278 +0,0 @@
|
|||
:version: $RCSfile: index.rst,v $ $Revision: 76e0bf38aaba $ $Date: 2011/03/22 00:48:41 $
|
||||
|
||||
.. default-role:: fs
|
||||
|
||||
===========================
|
||||
Setting up |Tesseractocr|
|
||||
===========================
|
||||
|
||||
The Visual Studio 2008 Solutions included with |Tesseractocr|, rely on
|
||||
*relative paths* to reference files and directories --- including
|
||||
locations that are *outside* of the `tesseract-3.0x` tree. It is
|
||||
therefore vitally important to correctly set up the directories for the
|
||||
various components. This section describes how to do this.
|
||||
|
||||
|
||||
.. _directory-setup:
|
||||
|
||||
Initial "Build" directory setup
|
||||
===============================
|
||||
|
||||
First create an empty directory where you will unpack all the required
|
||||
downloads. Assume you call this directory `C:\\BuildFolder`.
|
||||
|
||||
.. _download-leptonica:
|
||||
|
||||
1. Download the |Leptonica| 1.68 pre-built binary package
|
||||
(`leptonica-1.68-win32-lib-include-dirs.zip`) from:
|
||||
|
||||
http://code.google.com/p/leptonica/downloads/detail?name=leptonica-1.68-win32-lib-include-dirs.zip
|
||||
|
||||
and unpack it to `C:\\BuildFolder`.
|
||||
|
||||
2. |Leptonica|, even on Windows as of v1.68, still requires a few unix
|
||||
utilities (like `rm`, `diff`, `sleep`). The easiest way to deal with
|
||||
this is to follow the instructions at `Installing Cygwin coreutils
|
||||
<http://tpgit.github.com/UnOfficialLeptDocs/vs2008/installing-cygwin.html>`_.
|
||||
|
||||
At this point, if all you want to do is link with `libtesseract` you can
|
||||
`download <http://code.google.com/p/tesseract-ocr/downloads/list>`_ the
|
||||
file that just contains the "public" |Tesseractocr| headers along with
|
||||
the precompiled library binaries for Windows. Unpack it to
|
||||
`C:\\BuildFolder` and you'll now have::
|
||||
|
||||
C:\BuildFolder\
|
||||
|
||||
include\
|
||||
leptonica\
|
||||
tesseract\
|
||||
|
||||
leptonica_versionnumbers.vsprops
|
||||
tesseract_versionnumbers.vsprops
|
||||
|
||||
lib\
|
||||
giflib416-static-mtdll-debug.lib
|
||||
giflib416-static-mtdll.lib
|
||||
libjpeg8c-static-mtdll-debug.lib
|
||||
libjpeg8c-static-mtdll.lib
|
||||
liblept168-static-mtdll-debug.lib
|
||||
liblept168-static-mtdll.lib
|
||||
liblept168.dll
|
||||
liblept168.lib
|
||||
liblept168d.dll
|
||||
liblept168d.lib
|
||||
libpng143-static-mtdll-debug.lib
|
||||
libpng143-static-mtdll.lib
|
||||
libtesseract302.dll
|
||||
libtesseract302.lib
|
||||
libtesseract302d.dll
|
||||
libtesseract302d.lib
|
||||
libtesseract302-static.lib
|
||||
libtesseract302-static-debug.lib
|
||||
libtiff394-static-mtdll-debug.lib
|
||||
libtiff394-static-mtdll.lib
|
||||
zlib125-static-mtdll-debug.lib
|
||||
zlib125-static-mtdll.lib
|
||||
|
||||
and you can skip the rest of this page and go directly to
|
||||
:doc:`programming`.
|
||||
|
||||
The recommended action, however, is to download the |Tesseractocr|
|
||||
sources and build them yourself. Therefore...
|
||||
|
||||
3. Download the |Tesseractocr| Visual Studio 2008 source files from the
|
||||
`downloads page
|
||||
<http://code.google.com/p/tesseract-ocr/downloads/list>`_. If, for
|
||||
example, you'd like to build v3.02 you would use the following link:
|
||||
|
||||
http://code.google.com/p/tesseract-ocr/downloads/detail?name=tesseract-ocr-3.02-vs2008.zip
|
||||
|
||||
Unpack the file to `C:\\BuildFolder`
|
||||
|
||||
You would now have the following directory structure::
|
||||
|
||||
C:\BuildFolder\
|
||||
|
||||
include\
|
||||
leptonica\
|
||||
|
||||
leptonica_versionnumbers.vsprops
|
||||
tesseract_versionnumbers.vsprops
|
||||
|
||||
lib\
|
||||
giflib416-static-mtdll-debug.lib
|
||||
giflib416-static-mtdll.lib
|
||||
libjpeg8c-static-mtdll-debug.lib
|
||||
libjpeg8c-static-mtdll.lib
|
||||
liblept168-static-mtdll-debug.lib
|
||||
liblept168-static-mtdll.lib
|
||||
liblept168.dll
|
||||
liblept168.lib
|
||||
liblept168d.dll
|
||||
liblept168d.lib
|
||||
libpng143-static-mtdll-debug.lib
|
||||
libpng143-static-mtdll.lib
|
||||
libtiff394-static-mtdll-debug.lib
|
||||
libtiff394-static-mtdll.lib
|
||||
zlib125-static-mtdll-debug.lib
|
||||
zlib125-static-mtdll.lib
|
||||
|
||||
tesseract-3.02\
|
||||
vs2008\
|
||||
ambiguous_words\
|
||||
classifier_tester\
|
||||
cntraining\
|
||||
combine_tessdata\
|
||||
dawg2wordlist\
|
||||
doc\
|
||||
include\
|
||||
libtesseract\
|
||||
libtesseract.vcproj
|
||||
mftraining\
|
||||
port\
|
||||
shapeclustering\
|
||||
sphinx\
|
||||
tesseract\
|
||||
tesseract.vcproj
|
||||
unicharset_extractor\
|
||||
wordlist2dawg\
|
||||
|
||||
tesseract.sln
|
||||
tesshelper.py
|
||||
|
||||
4. Download the |Tesseractocr| source files for the same version as the
|
||||
VS2008 files you just unpacked. In this case, the proper link would
|
||||
be:
|
||||
|
||||
http://code.google.com/p/tesseract-ocr/downloads/detail?name=tesseract-3.02.tar.gz
|
||||
|
||||
Unpack the file to `C:\\BuildFolder`
|
||||
|
||||
This will add a bunch of directories to your already existing
|
||||
`C:\\BuildFolder\\tesseract-3.0x` directory. You should now have (for
|
||||
v3.02)::
|
||||
|
||||
C:\BuildFolder\
|
||||
|
||||
include\
|
||||
leptonica\
|
||||
lib\
|
||||
tesseract-3.02\
|
||||
api\
|
||||
ccmain\
|
||||
ccstruct\
|
||||
ccutil\
|
||||
classify\
|
||||
config\
|
||||
contrib\
|
||||
cube\
|
||||
cutil\
|
||||
dict\
|
||||
doc\
|
||||
image\
|
||||
java\
|
||||
image\
|
||||
neural_networks\
|
||||
tessdata\
|
||||
testing\
|
||||
textord\
|
||||
training\
|
||||
viewer\
|
||||
vs2008\
|
||||
wordrec\
|
||||
|
||||
.. _copying-headers:
|
||||
|
||||
If you are planning on writing applications that link with
|
||||
|Tesseractocr|, and you don't want to add all the `tesseract-3.0x`
|
||||
directories to your project's list of ``include`` directories, then do
|
||||
this additional step:
|
||||
|
||||
5. Copy all the required headers to the "public" include folder.
|
||||
|
||||
If you already have a `C:\\BuildFolder\\include\\tesseract`
|
||||
directory you should delete it in case some of the files have been
|
||||
removed.
|
||||
|
||||
Then use the python `tess-helper.py` script to copy (possibly updated
|
||||
versions of) the required headers by doing::
|
||||
|
||||
cd C:\BuildFolder\tesseract-3.02\vs2008
|
||||
python tesshelper.py .. copy ..\..\include
|
||||
|
||||
See :ref:`tesshelper` for more details.
|
||||
|
||||
You are now ready to :doc:`build <building>` |Tesseractocr| using Visual
|
||||
Studio 2008.
|
||||
|
||||
|
||||
.. _using-latest-sources:
|
||||
|
||||
Using the latest |Tesseractocr| sources
|
||||
=======================================
|
||||
|
||||
If you'd like to try the absolute latest version of |Tesseractocr|,
|
||||
here's how to download the source files from its SVN repository:
|
||||
|
||||
1. Follow Steps 1 and 2 :ref:`above <directory-setup>`.
|
||||
|
||||
#. `Checkout <http://code.google.com/p/tesseract-ocr/source/checkout>`_
|
||||
the |Tesseractocr| sources to a directory on your computer. This
|
||||
directory should :bi:`not` be `C:\\BuildFolder`!
|
||||
|
||||
If you are unfamiliar with `SVN <http://subversion.apache.org/>`_,
|
||||
the easiest way to do this is to first download and install
|
||||
`TortoiseSVN <http://tortoisesvn.net/>`_ and then:
|
||||
|
||||
a. Right-click the (empty) directory where you want the working copy
|
||||
and choose :menuselection:`SVN Chec&kout...` from
|
||||
the pop-up menu.
|
||||
|
||||
#. Enter ``http://tesseract-ocr.googlecode.com/svn/trunk/`` for
|
||||
:guilabel:`&URL of repository`. You can keep all the other
|
||||
settings at their defaults.
|
||||
|
||||
.. image:: images/tortoisesvn_checkout.png
|
||||
:align: center
|
||||
:alt: TortoiseSVN Checkout Dialog Box
|
||||
|
||||
#. Click the :guilabel:`&OK` button to commence downloading the
|
||||
|Tesseractocr| sources to your computer. This might take a while as
|
||||
the language data in the `tessdata` directory is quite large. As
|
||||
of February 2012, about 335MB needs to be transferred for the
|
||||
initial checkout. The total size of the resulting working copy is
|
||||
about 1.2GB.
|
||||
|
||||
#. Keeping your working copy up to date after this is as simple as
|
||||
right-clicking its directory and choosing :menuselection:`SVN
|
||||
&Update`. Unlike the initial checkout, this will usually finish
|
||||
very quickly.
|
||||
|
||||
#. Copy the :bi:`contents` of your working directory, except for the
|
||||
`tessdata` directory, to `C:\\BuildFolder\\tesseract-3.0x`, where
|
||||
``x`` should probably be the latest stable release + ``alpha``,
|
||||
``beta``, etc.
|
||||
|
||||
#. Optionally, follow Step 5 from :ref:`above <copying-headers>`.
|
||||
|
||||
#. You'll probably want to set an environment varible named
|
||||
``TESSDATA_PREFIX`` to point at your working copy directory (since
|
||||
that now contains the latest `tessdata` directory).
|
||||
|
||||
#. If someone hasn't already done so, you have to proceed to
|
||||
:ref:`updating-vs2008-directory`. You can skip all the steps that
|
||||
relate to updating the version number. Otherwise, depending on how
|
||||
many changes have been made since the last stable release, you may
|
||||
have little or no work to do.
|
||||
|
||||
..
|
||||
Local Variables:
|
||||
coding: utf-8
|
||||
mode: rst
|
||||
indent-tabs-mode: nil
|
||||
sentence-end-double-space: t
|
||||
fill-column: 72
|
||||
mode: auto-fill
|
||||
standard-indent: 3
|
||||
tab-stop-list: (3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60)
|
||||
End:
|
|
@ -1,55 +0,0 @@
|
|||
/*====================================================================*
|
||||
- Copyright (C) 2001 Leptonica. All rights reserved.
|
||||
- This software is distributed in the hope that it will be
|
||||
- useful, but with NO WARRANTY OF ANY KIND.
|
||||
- No author or distributor accepts responsibility to anyone for the
|
||||
- consequences of using this software, or for whether it serves any
|
||||
- particular purpose or works at all, unless he or she says so in
|
||||
- writing. Everyone is granted permission to copy, modify and
|
||||
- redistribute this source code, for commercial or non-commercial
|
||||
- purposes, with the following restrictions: (1) the origin of this
|
||||
- source code must not be misrepresented; (2) modified versions must
|
||||
- be plainly marked as such; and (3) this notice may not be removed
|
||||
- or altered from any source or modified source distribution.
|
||||
*====================================================================*/
|
||||
|
||||
#ifndef LEPTONICA_STACK_H
|
||||
#define LEPTONICA_STACK_H
|
||||
|
||||
/*
|
||||
* stack.h
|
||||
*
|
||||
* Expandable pointer stack for arbitrary void* data.
|
||||
*
|
||||
* The L_Stack is an array of void * ptrs, onto which arbitrary
|
||||
* objects can be stored. At any time, the number of
|
||||
* stored objects is stack->n. The object at the bottom
|
||||
* of the stack is at array[0]; the object at the top of
|
||||
* the stack is at array[n-1]. New objects are added
|
||||
* to the top of the stack, at the first available location,
|
||||
* which is array[n]. Objects are removed from the top of the
|
||||
* stack. When an attempt is made to remove an object from an
|
||||
* empty stack, the result is null. When the stack becomes
|
||||
* filled, so that n = nalloc, the size is doubled.
|
||||
*
|
||||
* The auxiliary stack can be used to store and remove
|
||||
* objects for re-use. It must be created by a separate
|
||||
* call to pstackCreate(). [Just imagine the chaos if
|
||||
* pstackCreate() created the auxiliary stack!]
|
||||
* pstackDestroy() checks for the auxiliary stack and removes it.
|
||||
*/
|
||||
|
||||
|
||||
/* Note that array[n] is the first null ptr in the array */
|
||||
struct L_Stack
|
||||
{
|
||||
l_int32 nalloc; /* size of ptr array */
|
||||
l_int32 n; /* number of stored elements */
|
||||
void **array; /* ptr array */
|
||||
struct L_Stack *auxstack; /* auxiliary stack */
|
||||
};
|
||||
typedef struct L_Stack L_STACK;
|
||||
|
||||
|
||||
#endif /* LEPTONICA_STACK_H */
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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
|
||||
|
||||
#include <stdio.h>
|
||||
/*
|
||||
** 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
|
||||
|
||||
#include "imgtiff.h"
|
||||
#include "helpers.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
|
||||
|
||||
|
||||
// CountTiffPages
|
||||
// Returns the number of pages in the file if it is a tiff file, otherwise 0.
|
||||
// WARNING: requires __MOTO__ to be #defined on a big-endian system.
|
||||
// On linux this is handled by configure - see above.
|
||||
int CountTiffPages(FILE* fp) {
|
||||
if (fp == NULL) return 0;
|
||||
// Read header
|
||||
inT16 filetype = 0;
|
||||
if (fread(&filetype, sizeof(filetype), 1, fp) != 1 ||
|
||||
(filetype != INTEL && filetype != MOTO)) {
|
||||
return 0;
|
||||
}
|
||||
fseek(fp, 4L, SEEK_SET);
|
||||
int npages = 0;
|
||||
do {
|
||||
inT32 start; // Start of tiff directory.
|
||||
if (fread(&start, sizeof(start), 1, fp) != 1) {
|
||||
return npages;
|
||||
}
|
||||
if (filetype != __NATIVE__)
|
||||
ReverseN(&start, sizeof(start));
|
||||
if (start <= 0) {
|
||||
return npages;
|
||||
}
|
||||
fseek(fp, start, SEEK_SET);
|
||||
inT16 entries; // No of tiff entries.
|
||||
if (fread(&entries, sizeof(entries), 1, fp) != 1) {
|
||||
return npages;
|
||||
}
|
||||
if (filetype != __NATIVE__)
|
||||
ReverseN(&entries, sizeof(entries));
|
||||
// Skip the tags and get to the next start.
|
||||
fseek(fp, entries * sizeof(TIFFENTRY), SEEK_CUR);
|
||||
++npages;
|
||||
} while (1);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: word_size_model.cpp
|
||||
* Description: Implementation of the Word Size Model Class
|
||||
* Author: Ahmad Abdulkader
|
||||
* Created: 2008
|
||||
*
|
||||
* (C) Copyright 2008, 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 <math.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "word_size_model.h"
|
||||
#include "cube_utils.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
WordSizeModel::WordSizeModel(CharSet * char_set, bool contextual) {
|
||||
char_set_ = char_set;
|
||||
contextual_ = contextual;
|
||||
}
|
||||
|
||||
WordSizeModel::~WordSizeModel() {
|
||||
for (int fnt = 0; fnt < font_pair_size_models_.size(); fnt++) {
|
||||
FontPairSizeInfo fnt_info = font_pair_size_models_[fnt];
|
||||
delete []fnt_info.pair_size_info[0];
|
||||
delete []fnt_info.pair_size_info;
|
||||
}
|
||||
}
|
||||
|
||||
WordSizeModel *WordSizeModel::Create(const string &data_file_path,
|
||||
const string &lang,
|
||||
CharSet *char_set,
|
||||
bool contextual) {
|
||||
WordSizeModel *obj = new WordSizeModel(char_set, contextual);
|
||||
if (!obj) {
|
||||
fprintf(stderr, "Cube ERROR (WordSizeModel::Create): unable to allocate "
|
||||
"new word size model object\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!obj->Init(data_file_path, lang)) {
|
||||
delete obj;
|
||||
return NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool WordSizeModel::Init(const string &data_file_path, const string &lang) {
|
||||
string stats_file_name;
|
||||
stats_file_name = data_file_path + lang;
|
||||
stats_file_name += ".cube.size";
|
||||
|
||||
// read file to memory
|
||||
string str_data;
|
||||
|
||||
if (!CubeUtils::ReadFileToString(stats_file_name, &str_data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// split to words
|
||||
vector<string> tokens;
|
||||
CubeUtils::SplitStringUsing(str_data, "\t\r\n", &tokens);
|
||||
if (tokens.size() < 1) {
|
||||
fprintf(stderr, "Cube ERROR (WordSizeModel::Init): invalid "
|
||||
"file contents: %s\n", stats_file_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
font_pair_size_models_.clear();
|
||||
|
||||
// token count per line depends on whether the language is contextual or not
|
||||
int token_cnt = contextual_ ?
|
||||
(kExpectedTokenCount + 4) : kExpectedTokenCount;
|
||||
// the count of size classes depends on whether the language is contextual
|
||||
// or not. For non contextual languages (Ex: Eng), it is equal to the class
|
||||
// count. For contextual languages (Ex: Ara), it is equal to the class count
|
||||
// multiplied by the position count (4: start, middle, final, isolated)
|
||||
int size_class_cnt = contextual_ ?
|
||||
(char_set_->ClassCount() * 4) : char_set_->ClassCount();
|
||||
string fnt_name = "";
|
||||
|
||||
for (int tok = 0; tok < tokens.size(); tok += token_cnt) {
|
||||
// a new font, write the old font data and re-init
|
||||
if (tok == 0 || fnt_name != tokens[tok]) {
|
||||
FontPairSizeInfo fnt_info;
|
||||
|
||||
fnt_info.pair_size_info = new PairSizeInfo *[size_class_cnt];
|
||||
if (!fnt_info.pair_size_info) {
|
||||
fprintf(stderr, "Cube ERROR (WordSizeModel::Init): error allcoating "
|
||||
"memory for font pair size info\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fnt_info.pair_size_info[0] =
|
||||
new PairSizeInfo[size_class_cnt * size_class_cnt];
|
||||
if (!fnt_info.pair_size_info[0]) {
|
||||
fprintf(stderr, "Cube ERROR (WordSizeModel::Init): error allocating "
|
||||
"memory for font pair size info\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(fnt_info.pair_size_info[0], 0, size_class_cnt * size_class_cnt *
|
||||
sizeof(PairSizeInfo));
|
||||
|
||||
for (int cls = 1; cls < size_class_cnt; cls++) {
|
||||
fnt_info.pair_size_info[cls] =
|
||||
fnt_info.pair_size_info[cls - 1] + size_class_cnt;
|
||||
}
|
||||
|
||||
// strip out path and extension
|
||||
string stripped_font_name = tokens[tok].substr(0, tokens[tok].find('.'));
|
||||
string::size_type strt_pos = stripped_font_name.find_last_of("/\\");
|
||||
if (strt_pos != string::npos) {
|
||||
fnt_info.font_name = stripped_font_name.substr(strt_pos);
|
||||
} else {
|
||||
fnt_info.font_name = stripped_font_name;
|
||||
}
|
||||
font_pair_size_models_.push_back(fnt_info);
|
||||
}
|
||||
|
||||
// parse the data
|
||||
int cls_0;
|
||||
int cls_1;
|
||||
double delta_top;
|
||||
double wid_0;
|
||||
double hgt_0;
|
||||
double wid_1;
|
||||
double hgt_1;
|
||||
int size_code_0;
|
||||
int size_code_1;
|
||||
|
||||
// read and parse the tokens
|
||||
if (contextual_) {
|
||||
int start_0;
|
||||
int end_0;
|
||||
int start_1;
|
||||
int end_1;
|
||||
// The expected format for a character size bigram is as follows:
|
||||
// ClassId0<delim>Start-flag0<delim>End-flag0<delim>String0(ignored)
|
||||
// Width0<delim>Height0<delim>
|
||||
// ClassId1<delim>Start-flag1<delim>End-flag1<delim>String1(ignored)
|
||||
// HeightDelta<delim>Width1<delim>Height0<delim>
|
||||
// In case of non-contextual languages, the Start and End flags are
|
||||
// omitted
|
||||
if (sscanf(tokens[tok + 1].c_str(), "%d", &cls_0) != 1 ||
|
||||
sscanf(tokens[tok + 2].c_str(), "%d", &start_0) != 1 ||
|
||||
sscanf(tokens[tok + 3].c_str(), "%d", &end_0) != 1 ||
|
||||
sscanf(tokens[tok + 5].c_str(), "%lf", &wid_0) != 1 ||
|
||||
sscanf(tokens[tok + 6].c_str(), "%lf", &hgt_0) != 1 ||
|
||||
sscanf(tokens[tok + 7].c_str(), "%d", &cls_1) != 1 ||
|
||||
sscanf(tokens[tok + 8].c_str(), "%d", &start_1) != 1 ||
|
||||
sscanf(tokens[tok + 9].c_str(), "%d", &end_1) != 1 ||
|
||||
sscanf(tokens[tok + 11].c_str(), "%lf", &delta_top) != 1 ||
|
||||
sscanf(tokens[tok + 12].c_str(), "%lf", &wid_1) != 1 ||
|
||||
sscanf(tokens[tok + 13].c_str(), "%lf", &hgt_1) != 1 ||
|
||||
(start_0 != 0 && start_0 != 1) || (end_0 != 0 && end_0 != 1) ||
|
||||
(start_1 != 0 && start_1 != 1) || (end_1 != 0 && end_1 != 1)) {
|
||||
fprintf(stderr, "Cube ERROR (WordSizeModel::Init): bad format at "
|
||||
"line %d\n", 1 + (tok / token_cnt));
|
||||
return false;
|
||||
}
|
||||
size_code_0 = SizeCode(cls_0, start_0, end_0);
|
||||
size_code_1 = SizeCode(cls_1, start_1, end_1);
|
||||
} else {
|
||||
if (sscanf(tokens[tok + 1].c_str(), "%d", &cls_0) != 1 ||
|
||||
sscanf(tokens[tok + 3].c_str(), "%lf", &wid_0) != 1 ||
|
||||
sscanf(tokens[tok + 4].c_str(), "%lf", &hgt_0) != 1 ||
|
||||
sscanf(tokens[tok + 5].c_str(), "%d", &cls_1) != 1 ||
|
||||
sscanf(tokens[tok + 7].c_str(), "%lf", &delta_top) != 1 ||
|
||||
sscanf(tokens[tok + 8].c_str(), "%lf", &wid_1) != 1 ||
|
||||
sscanf(tokens[tok + 9].c_str(), "%lf", &hgt_1) != 1) {
|
||||
fprintf(stderr, "Cube ERROR (WordSizeModel::Init): bad format at "
|
||||
"line %d\n", 1 + (tok / token_cnt));
|
||||
return false;
|
||||
}
|
||||
size_code_0 = cls_0;
|
||||
size_code_1 = cls_1;
|
||||
}
|
||||
|
||||
// copy the data to the size tables
|
||||
FontPairSizeInfo fnt_info = font_pair_size_models_.back();
|
||||
fnt_info.pair_size_info[size_code_0][size_code_1].delta_top =
|
||||
static_cast<int>(delta_top * kShapeModelScale);
|
||||
fnt_info.pair_size_info[size_code_0][size_code_1].wid_0 =
|
||||
static_cast<int>(wid_0 * kShapeModelScale);
|
||||
fnt_info.pair_size_info[size_code_0][size_code_1].hgt_0 =
|
||||
static_cast<int>(hgt_0 * kShapeModelScale);
|
||||
fnt_info.pair_size_info[size_code_0][size_code_1].wid_1 =
|
||||
static_cast<int>(wid_1 * kShapeModelScale);
|
||||
fnt_info.pair_size_info[size_code_0][size_code_1].hgt_1 =
|
||||
static_cast<int>(hgt_1 * kShapeModelScale);
|
||||
|
||||
fnt_name = tokens[tok];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int WordSizeModel::Cost(CharSamp **samp_array, int samp_cnt) const {
|
||||
if (samp_cnt < 2) {
|
||||
return 0;
|
||||
}
|
||||
double best_dist = static_cast<double>(WORST_COST);
|
||||
int best_fnt = -1;
|
||||
for (int fnt = 0; fnt < font_pair_size_models_.size(); fnt++) {
|
||||
const FontPairSizeInfo *fnt_info = &font_pair_size_models_[fnt];
|
||||
double mean_dist = 0;
|
||||
int pair_cnt = 0;
|
||||
|
||||
for (int smp_0 = 0; smp_0 < samp_cnt; smp_0++) {
|
||||
int cls_0 = char_set_->ClassID(samp_array[smp_0]->StrLabel());
|
||||
if (cls_0 < 1) {
|
||||
continue;
|
||||
}
|
||||
// compute size code for samp 0 based on class id and position
|
||||
int size_code_0;
|
||||
if (contextual_) {
|
||||
size_code_0 = SizeCode(cls_0,
|
||||
samp_array[smp_0]->FirstChar() == 0 ? 0 : 1,
|
||||
samp_array[smp_0]->LastChar() == 0 ? 0 : 1);
|
||||
} else {
|
||||
size_code_0 = cls_0;
|
||||
}
|
||||
|
||||
int char0_height = samp_array[smp_0]->Height();
|
||||
int char0_width = samp_array[smp_0]->Width();
|
||||
int char0_top = samp_array[smp_0]->Top();
|
||||
|
||||
for (int smp_1 = smp_0 + 1; smp_1 < samp_cnt; smp_1++) {
|
||||
int cls_1 = char_set_->ClassID(samp_array[smp_1]->StrLabel());
|
||||
if (cls_1 < 1) {
|
||||
continue;
|
||||
}
|
||||
// compute size code for samp 0 based on class id and position
|
||||
int size_code_1;
|
||||
if (contextual_) {
|
||||
size_code_1 = SizeCode(cls_1,
|
||||
samp_array[smp_1]->FirstChar() == 0 ? 0 : 1,
|
||||
samp_array[smp_1]->LastChar() == 0 ? 0 : 1);
|
||||
} else {
|
||||
size_code_1 = cls_1;
|
||||
}
|
||||
double dist = PairCost(
|
||||
char0_width, char0_height, char0_top, samp_array[smp_1]->Width(),
|
||||
samp_array[smp_1]->Height(), samp_array[smp_1]->Top(),
|
||||
fnt_info->pair_size_info[size_code_0][size_code_1]);
|
||||
if (dist > 0) {
|
||||
mean_dist += dist;
|
||||
pair_cnt++;
|
||||
}
|
||||
} // smp_1
|
||||
} // smp_0
|
||||
if (pair_cnt == 0) {
|
||||
continue;
|
||||
}
|
||||
mean_dist /= pair_cnt;
|
||||
if (best_fnt == -1 || mean_dist < best_dist) {
|
||||
best_dist = mean_dist;
|
||||
best_fnt = fnt;
|
||||
}
|
||||
}
|
||||
if (best_fnt == -1) {
|
||||
return static_cast<int>(WORST_COST);
|
||||
} else {
|
||||
return static_cast<int>(best_dist);
|
||||
}
|
||||
}
|
||||
|
||||
double WordSizeModel::PairCost(int width_0, int height_0, int top_0,
|
||||
int width_1, int height_1, int top_1,
|
||||
const PairSizeInfo& pair_info) {
|
||||
double scale_factor = static_cast<double>(pair_info.hgt_0) /
|
||||
static_cast<double>(height_0);
|
||||
double dist = 0.0;
|
||||
if (scale_factor > 0) {
|
||||
double norm_width_0 = width_0 * scale_factor;
|
||||
double norm_width_1 = width_1 * scale_factor;
|
||||
double norm_height_1 = height_1 * scale_factor;
|
||||
double norm_delta_top = (top_1 - top_0) * scale_factor;
|
||||
|
||||
// accumulate the distance between the model character and the
|
||||
// predicted one on all dimensions of the pair
|
||||
dist += fabs(pair_info.wid_0 - norm_width_0);
|
||||
dist += fabs(pair_info.wid_1 - norm_width_1);
|
||||
dist += fabs(pair_info.hgt_1 - norm_height_1);
|
||||
dist += fabs(pair_info.delta_top - norm_delta_top);
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
} // namespace tesseract
|
|
@ -1,117 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4244) // Conversion warnings
|
||||
#endif
|
||||
|
||||
#include "mfcpch.h"
|
||||
#include "tfacep.h"
|
||||
#include "tfacepp.h"
|
||||
#include "tessbox.h"
|
||||
#include "mfoutline.h"
|
||||
#include "tesseractclass.h"
|
||||
|
||||
#define EXTERN
|
||||
|
||||
/**
|
||||
* @name tess_segment_pass1
|
||||
*
|
||||
* Segment a word using the pass1 conditions of the tess segmenter.
|
||||
* @param word word to do
|
||||
* @param blob_choices list of blob lists
|
||||
*/
|
||||
|
||||
namespace tesseract {
|
||||
void Tesseract::tess_segment_pass1(WERD_RES *word,
|
||||
BLOB_CHOICE_LIST_CLIST *blob_choices) {
|
||||
int saved_enable_assoc = 0;
|
||||
int saved_chop_enable = 0;
|
||||
|
||||
if (word->word->flag(W_DONT_CHOP)) {
|
||||
saved_enable_assoc = wordrec_enable_assoc;
|
||||
saved_chop_enable = chop_enable;
|
||||
wordrec_enable_assoc.set_value(0);
|
||||
chop_enable.set_value(0);
|
||||
if (word->word->flag(W_REP_CHAR))
|
||||
getDict().permute_only_top.set_value(true);
|
||||
}
|
||||
set_pass1();
|
||||
recog_word(word, blob_choices);
|
||||
if (word->word->flag(W_DONT_CHOP)) {
|
||||
wordrec_enable_assoc.set_value(saved_enable_assoc);
|
||||
chop_enable.set_value(saved_chop_enable);
|
||||
getDict().permute_only_top.set_value(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @name tess_segment_pass2
|
||||
*
|
||||
* Segment a word using the pass2 conditions of the tess segmenter.
|
||||
* @param word word to do
|
||||
* @param blob_choices list of blob lists
|
||||
*/
|
||||
|
||||
void Tesseract::tess_segment_pass2(WERD_RES *word,
|
||||
BLOB_CHOICE_LIST_CLIST *blob_choices) {
|
||||
int saved_enable_assoc = 0;
|
||||
int saved_chop_enable = 0;
|
||||
|
||||
if (word->word->flag(W_DONT_CHOP)) {
|
||||
saved_enable_assoc = wordrec_enable_assoc;
|
||||
saved_chop_enable = chop_enable;
|
||||
wordrec_enable_assoc.set_value(0);
|
||||
chop_enable.set_value(0);
|
||||
if (word->word->flag(W_REP_CHAR))
|
||||
getDict().permute_only_top.set_value(true);
|
||||
}
|
||||
set_pass2();
|
||||
recog_word(word, blob_choices);
|
||||
if (word->word->flag(W_DONT_CHOP)) {
|
||||
wordrec_enable_assoc.set_value(saved_enable_assoc);
|
||||
chop_enable.set_value(saved_chop_enable);
|
||||
getDict().permute_only_top.set_value(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name tess_acceptable_word
|
||||
*
|
||||
* @return true if the word is regarded as "good enough".
|
||||
* @param word_choice after context
|
||||
* @param raw_choice before context
|
||||
*/
|
||||
BOOL8 Tesseract::tess_acceptable_word(
|
||||
WERD_CHOICE *word_choice, // after context
|
||||
WERD_CHOICE *raw_choice) { // before context
|
||||
return getDict().AcceptableResult(*word_choice);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @name tess_add_doc_word
|
||||
*
|
||||
* Add the given word to the document dictionary
|
||||
*/
|
||||
void Tesseract::tess_add_doc_word(WERD_CHOICE *word_choice) {
|
||||
getDict().add_document_word(*word_choice);
|
||||
}
|
||||
} // namespace tesseract
|
|
@ -1,102 +0,0 @@
|
|||
/* -*-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 <assert.h>
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
F u n c t i o n s
|
||||
----------------------------------------------------------------------*/
|
||||
/**********************************************************************
|
||||
* 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;
|
||||
// This doesn't leak - the outlines share the EDGEPTs.
|
||||
other_outline->loop = NULL;
|
||||
delete other_outline;
|
||||
other_outline = last_outline;
|
||||
// If it is part of a cut, then it can't be a hole any more.
|
||||
outline->is_hole = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* 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) {
|
||||
outline->ComputeBoundingBox();
|
||||
}
|
||||
}
|
|
@ -1,945 +0,0 @@
|
|||
/*====================================================================*
|
||||
- Copyright (C) 2001 Leptonica. All rights reserved.
|
||||
- This software is distributed in the hope that it will be
|
||||
- useful, but with NO WARRANTY OF ANY KIND.
|
||||
- No author or distributor accepts responsibility to anyone for the
|
||||
- consequences of using this software, or for whether it serves any
|
||||
- particular purpose or works at all, unless he or she says so in
|
||||
- writing. Everyone is granted permission to copy, modify and
|
||||
- redistribute this source code, for commercial or non-commercial
|
||||
- purposes, with the following restrictions: (1) the origin of this
|
||||
- source code must not be misrepresented; (2) modified versions must
|
||||
- be plainly marked as such; and (3) this notice may not be removed
|
||||
- or altered from any source or modified source distribution.
|
||||
*====================================================================*/
|
||||
|
||||
#ifndef LEPTONICA_PIX_H
|
||||
#define LEPTONICA_PIX_H
|
||||
|
||||
/*
|
||||
* pix.h
|
||||
*
|
||||
* Contains the following structures:
|
||||
* struct Pix
|
||||
* struct PixColormap
|
||||
* struct RGBA_Quad
|
||||
* struct Pixa
|
||||
* struct Pixaa
|
||||
* struct Box
|
||||
* struct Boxa
|
||||
* struct Boxaa
|
||||
* struct Pta
|
||||
* struct Ptaa
|
||||
* struct Pixacc
|
||||
* struct PixTiling
|
||||
* struct FPix
|
||||
* struct FPixa
|
||||
* struct DPix
|
||||
* struct PixComp
|
||||
* struct PixaComp
|
||||
*
|
||||
* Contains definitions for:
|
||||
* Colors for RGB
|
||||
* Perceptual color weights
|
||||
* Colormap conversion flags
|
||||
* Rasterop bit flags
|
||||
* Structure access flags (for insert, copy, clone, copy-clone)
|
||||
* Sorting flags (by type and direction)
|
||||
* Blending flags
|
||||
* Graphics pixel setting flags
|
||||
* Size filtering flags
|
||||
* Color component selection flags
|
||||
* Rotation and shear flags
|
||||
* Affine transform order flags
|
||||
* Grayscale filling flags
|
||||
* Flags for setting to white or black
|
||||
* Dithering flags
|
||||
* Distance flags
|
||||
* Statistical measures
|
||||
* Set selection flags
|
||||
* Text orientation flags
|
||||
* Edge orientation flags
|
||||
* Line orientation flags
|
||||
* Scan direction flags
|
||||
* Horizontal warp
|
||||
* Pixel selection for resampling
|
||||
* Thinning flags
|
||||
* Runlength flags
|
||||
* Edge filter flags
|
||||
* Handling negative values in conversion to unsigned int
|
||||
* Subpixel color component ordering in LCD display
|
||||
* Relative to zero flags
|
||||
* HSV histogram flags
|
||||
* Region flags (inclusion, exclusion)
|
||||
* Flags for adding text to a pix
|
||||
* Flags for selecting display program
|
||||
*/
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Basic Pix *
|
||||
*-------------------------------------------------------------------------*/
|
||||
struct Pix
|
||||
{
|
||||
l_uint32 w; /* width in pixels */
|
||||
l_uint32 h; /* height in pixels */
|
||||
l_uint32 d; /* depth in bits */
|
||||
l_uint32 wpl; /* 32-bit words/line */
|
||||
l_uint32 refcount; /* reference count (1 if no clones) */
|
||||
l_int32 xres; /* image res (ppi) in x direction */
|
||||
/* (use 0 if unknown) */
|
||||
l_int32 yres; /* image res (ppi) in y direction */
|
||||
/* (use 0 if unknown) */
|
||||
l_int32 informat; /* input file format, IFF_* */
|
||||
char *text; /* text string associated with pix */
|
||||
struct PixColormap *colormap; /* colormap (may be null) */
|
||||
l_uint32 *data; /* the image data */
|
||||
};
|
||||
typedef struct Pix PIX;
|
||||
|
||||
|
||||
struct PixColormap
|
||||
{
|
||||
void *array; /* colormap table (array of RGBA_QUAD) */
|
||||
l_int32 depth; /* of pix (1, 2, 4 or 8 bpp) */
|
||||
l_int32 nalloc; /* number of color entries allocated */
|
||||
l_int32 n; /* number of color entries used */
|
||||
};
|
||||
typedef struct PixColormap PIXCMAP;
|
||||
|
||||
|
||||
/* Colormap table entry (after the BMP version).
|
||||
* Note that the BMP format stores the colormap table exactly
|
||||
* as it appears here, with color samples being stored sequentially,
|
||||
* in the order (b,g,r,a). */
|
||||
struct RGBA_Quad
|
||||
{
|
||||
l_uint8 blue;
|
||||
l_uint8 green;
|
||||
l_uint8 red;
|
||||
l_uint8 reserved;
|
||||
};
|
||||
typedef struct RGBA_Quad RGBA_QUAD;
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Colors for 32 bpp *
|
||||
*-------------------------------------------------------------------------*/
|
||||
/* Notes:
|
||||
* (1) These are the byte indices for colors in 32 bpp images.
|
||||
* They are used through the GET/SET_DATA_BYTE accessors.
|
||||
* The 4th byte, typically known as the "alpha channel" and used
|
||||
* for blending, is not explicitly used in leptonica.
|
||||
* (2) If you redefine these values, functions that have the shifts
|
||||
* hardcoded (instead of using the constants below) will break.
|
||||
* These functions are labelled with "***" next to their names
|
||||
* at the top of the files in which they are defined.
|
||||
* Advice: Do not change these values!
|
||||
* (3) The shifts to extract the red, green and blue components
|
||||
* from a 32 bit pixel are defined in terms of these colors.
|
||||
*/
|
||||
enum {
|
||||
COLOR_RED = 0,
|
||||
COLOR_GREEN = 1,
|
||||
COLOR_BLUE = 2,
|
||||
L_ALPHA_CHANNEL = 3
|
||||
};
|
||||
|
||||
static const l_int32 L_RED_SHIFT =
|
||||
8 * (sizeof(l_uint32) - 1 - COLOR_RED); /* 24 */
|
||||
static const l_int32 L_GREEN_SHIFT =
|
||||
8 * (sizeof(l_uint32) - 1 - COLOR_GREEN); /* 16 */
|
||||
static const l_int32 L_BLUE_SHIFT =
|
||||
8 * (sizeof(l_uint32) - 1 - COLOR_BLUE); /* 8 */
|
||||
static const l_int32 L_ALPHA_SHIFT =
|
||||
8 * (sizeof(l_uint32) - 1 - L_ALPHA_CHANNEL); /* 0 */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Perceptual color weights *
|
||||
*-------------------------------------------------------------------------*/
|
||||
/* Notes:
|
||||
* (1) These numbers are ad-hoc, but they do add up to 1.
|
||||
* Unlike, for example, the weighting factor for conversion
|
||||
* of RGB to luminance, or more specifically to Y in the
|
||||
* YUV colorspace. Those numbers come from the
|
||||
* International Telecommunications Union, via ITU-R.
|
||||
*/
|
||||
static const l_float32 L_RED_WEIGHT = 0.3;
|
||||
static const l_float32 L_GREEN_WEIGHT = 0.5;
|
||||
static const l_float32 L_BLUE_WEIGHT = 0.2;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Flags for colormap conversion *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
REMOVE_CMAP_TO_BINARY = 0,
|
||||
REMOVE_CMAP_TO_GRAYSCALE = 1,
|
||||
REMOVE_CMAP_TO_FULL_COLOR = 2,
|
||||
REMOVE_CMAP_BASED_ON_SRC = 3
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
*
|
||||
* The following operation bit flags have been modified from
|
||||
* Sun's pixrect.h.
|
||||
*
|
||||
* The 'op' in 'rasterop' is represented by an integer
|
||||
* composed with Boolean functions using the set of five integers
|
||||
* given below. The integers, and the op codes resulting from
|
||||
* boolean expressions on them, need only be in the range from 0 to 15.
|
||||
* The function is applied on a per-pixel basis.
|
||||
*
|
||||
* Examples: the op code representing ORing the src and dest
|
||||
* is computed using the bit OR, as PIX_SRC | PIX_DST; the op
|
||||
* code representing XORing src and dest is found from
|
||||
* PIX_SRC ^ PIX_DST; the op code representing ANDing src and dest
|
||||
* is found from PIX_SRC & PIX_DST. Note that
|
||||
* PIX_NOT(PIX_CLR) = PIX_SET, and v.v., as they must be.
|
||||
*
|
||||
* We would like to use the following set of definitions:
|
||||
*
|
||||
* #define PIX_SRC 0xc
|
||||
* #define PIX_DST 0xa
|
||||
* #define PIX_NOT(op) ((op) ^ 0xf)
|
||||
* #define PIX_CLR 0x0
|
||||
* #define PIX_SET 0xf
|
||||
*
|
||||
* Now, these definitions differ from Sun's, in that Sun
|
||||
* left-shifted each value by 1 pixel, and used the least
|
||||
* significant bit as a flag for the "pseudo-operation" of
|
||||
* clipping. We don't need this bit, because it is both
|
||||
* efficient and safe ALWAYS to clip the rectangles to the src
|
||||
* and dest images, which is what we do. See the notes in rop.h
|
||||
* on the general choice of these bit flags.
|
||||
*
|
||||
* However, if you include Sun's xview package, you will get their
|
||||
* definitions, and because I like using these flags, we will
|
||||
* adopt the original Sun definitions to avoid redefinition conflicts.
|
||||
*
|
||||
* Then we have, for reference, the following 16 unique op flags:
|
||||
*
|
||||
* PIX_CLR 00000 0x0
|
||||
* PIX_SET 11110 0x1e
|
||||
* PIX_SRC 11000 0x18
|
||||
* PIX_DST 10100 0x14
|
||||
* PIX_NOT(PIX_SRC) 00110 0x06
|
||||
* PIX_NOT(PIX_DST) 01010 0x0a
|
||||
* PIX_SRC | PIX_DST 11100 0x1c
|
||||
* PIX_SRC & PIX_DST 10000 0x10
|
||||
* PIX_SRC ^ PIX_DST 01100 0x0c
|
||||
* PIX_NOT(PIX_SRC) | PIX_DST 10110 0x16
|
||||
* PIX_NOT(PIX_SRC) & PIX_DST 00100 0x04
|
||||
* PIX_SRC | PIX_NOT(PIX_DST) 11010 0x1a
|
||||
* PIX_SRC & PIX_NOT(PIX_DST) 01000 0x08
|
||||
* PIX_NOT(PIX_SRC | PIX_DST) 00010 0x02
|
||||
* PIX_NOT(PIX_SRC & PIX_DST) 01110 0x0e
|
||||
* PIX_NOT(PIX_SRC ^ PIX_DST) 10010 0x12
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
#define PIX_SRC (0xc << 1)
|
||||
#define PIX_DST (0xa << 1)
|
||||
#define PIX_NOT(op) ((op) ^ 0x1e)
|
||||
#define PIX_CLR (0x0 << 1)
|
||||
#define PIX_SET (0xf << 1)
|
||||
|
||||
#define PIX_PAINT (PIX_SRC | PIX_DST)
|
||||
#define PIX_MASK (PIX_SRC & PIX_DST)
|
||||
#define PIX_SUBTRACT (PIX_DST & PIX_NOT(PIX_SRC))
|
||||
#define PIX_XOR (PIX_SRC ^ PIX_DST)
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
*
|
||||
* Important Notes:
|
||||
*
|
||||
* (1) The image data is stored in a single contiguous
|
||||
* array of l_uint32, into which the pixels are packed.
|
||||
* By "packed" we mean that there are no unused bits
|
||||
* between pixels, except for end-of-line padding to
|
||||
* satisfy item (2) below.
|
||||
*
|
||||
* (2) Every image raster line begins on a 32-bit word
|
||||
* boundary within this array.
|
||||
*
|
||||
* (3) Pix image data is stored in 32-bit units, with the
|
||||
* pixels ordered from left to right in the image being
|
||||
* stored in order from the MSB to LSB within the word,
|
||||
* for both big-endian and little-endian machines.
|
||||
* This is the natural ordering for big-endian machines,
|
||||
* as successive bytes are stored and fetched progressively
|
||||
* to the right. However, for little-endians, when storing
|
||||
* we re-order the bytes from this byte stream order, and
|
||||
* reshuffle again for byte access on 32-bit entities.
|
||||
* So if the bytes come in sequence from left to right, we
|
||||
* store them on little-endians in byte order:
|
||||
* 3 2 1 0 7 6 5 4 ...
|
||||
* This MSB to LSB ordering allows left and right shift
|
||||
* operations on 32 bit words to move the pixels properly.
|
||||
*
|
||||
* (4) For 24-bit color images, use 32 bpp data, leaving
|
||||
* the fourth byte unused. Within each 4 byte pixel, the
|
||||
* colors are ordered from MSB to LSB, as follows:
|
||||
*
|
||||
* | MSB | 2nd MSB | 3rd MSB | LSB |
|
||||
* red green blue unused
|
||||
* 0 1 2 3 (big-endian)
|
||||
* 3 2 1 0 (little-endian)
|
||||
*
|
||||
* Because we use MSB to LSB ordering within the 32-bit word,
|
||||
* the individual 8-bit samples can be accessed with
|
||||
* GET_DATA_BYTE and SET_DATA_BYTE macros, using the
|
||||
* (implicitly big-ending) ordering
|
||||
* red: byte 0 (MSB)
|
||||
* green: byte 1 (2nd MSB)
|
||||
* blue: byte 2 (3rd MSB)
|
||||
*
|
||||
* This specific color assignment is made in this file,
|
||||
* through the definitions of COLOR_RED, etc. Then the R, G
|
||||
* and B sample values can be retrieved using
|
||||
* redval = GET_DATA_BYTE(&pixel, COLOR_RED);
|
||||
* greenval = GET_DATA_BYTE(&pixel, COLOR_GREEN);
|
||||
* blueval = GET_DATA_BYTE(&pixel, COLOR_BLUE);
|
||||
* and they can be set with
|
||||
* SET_DATA_BYTE(&pixel, COLOR_RED, redval);
|
||||
* SET_DATA_BYTE(&pixel, COLOR_GREEN, greenval);
|
||||
* SET_DATA_BYTE(&pixel, COLOR_BLUE, blueval);
|
||||
*
|
||||
* For extra speed we extract the R, G and B colors directly
|
||||
* by shifting and masking, explicitly using the values in
|
||||
* L_RED_SHIFT, L_GREEN_SHIFT and L_BLUE_SHIFT:
|
||||
* (pixel32 >> L_RED_SHIFT) & 0xff; (red)
|
||||
* (pixel32 >> L_GREEN_SHIFT) & 0xff; (green)
|
||||
* (pixel32 >> L_BLUE_SHIFT) & 0xff; (blue)
|
||||
* All these operations work properly on both big- and little-endians.
|
||||
*
|
||||
* For a few situations, these color shift values are hard-coded.
|
||||
* Changing the RGB color component ordering through the assignments
|
||||
* in this file will cause functions marked with "***" to fail.
|
||||
*
|
||||
* (5) A reference count is held within each pix, giving the
|
||||
* number of ptrs to the pix. When a pixClone() call
|
||||
* is made, the ref count is increased by 1, and
|
||||
* when a pixDestroy() call is made, the reference count
|
||||
* of the pix is decremented. The pix is only destroyed
|
||||
* when the reference count goes to zero.
|
||||
*
|
||||
* (6) The version numbers (below) are used in the serialization
|
||||
* of these data structures. They are placed in the files,
|
||||
* and rarely (if ever) change. Provision is currently made for
|
||||
* backward compatibility in reading from boxaa version 2.
|
||||
*
|
||||
* (7) The serialization dependencies are as follows:
|
||||
* pixaa : pixa : boxa
|
||||
* boxaa : boxa
|
||||
* So, for example, pixaa and boxaa can be changed without
|
||||
* forcing a change in pixa or boxa. However, if pixa is
|
||||
* changed, it forces a change in pixaa, and if boxa is
|
||||
* changed, if forces a change in the other three.
|
||||
* We define four version numbers:
|
||||
* PIXAA_VERSION_NUMBER
|
||||
* PIXA_VERSION_NUMBER
|
||||
* BOXAA_VERSION_NUMBER
|
||||
* BOXA_VERSION_NUMBER
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Array of pix *
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Serialization for primary data structures */
|
||||
#define PIXAA_VERSION_NUMBER 2
|
||||
#define PIXA_VERSION_NUMBER 2
|
||||
#define BOXA_VERSION_NUMBER 2
|
||||
#define BOXAA_VERSION_NUMBER 3
|
||||
|
||||
|
||||
struct Pixa
|
||||
{
|
||||
l_int32 n; /* number of Pix in ptr array */
|
||||
l_int32 nalloc; /* number of Pix ptrs allocated */
|
||||
l_uint32 refcount; /* reference count (1 if no clones) */
|
||||
struct Pix **pix; /* the array of ptrs to pix */
|
||||
struct Boxa *boxa; /* array of boxes */
|
||||
};
|
||||
typedef struct Pixa PIXA;
|
||||
|
||||
|
||||
struct Pixaa
|
||||
{
|
||||
l_int32 n; /* number of Pixa in ptr array */
|
||||
l_int32 nalloc; /* number of Pixa ptrs allocated */
|
||||
struct Pixa **pixa; /* array of ptrs to pixa */
|
||||
struct Boxa *boxa; /* array of boxes */
|
||||
};
|
||||
typedef struct Pixaa PIXAA;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Basic rectangle and rectangle arrays *
|
||||
*-------------------------------------------------------------------------*/
|
||||
struct Box
|
||||
{
|
||||
l_int32 x;
|
||||
l_int32 y;
|
||||
l_int32 w;
|
||||
l_int32 h;
|
||||
l_uint32 refcount; /* reference count (1 if no clones) */
|
||||
|
||||
};
|
||||
typedef struct Box BOX;
|
||||
|
||||
struct Boxa
|
||||
{
|
||||
l_int32 n; /* number of box in ptr array */
|
||||
l_int32 nalloc; /* number of box ptrs allocated */
|
||||
l_uint32 refcount; /* reference count (1 if no clones) */
|
||||
struct Box **box; /* box ptr array */
|
||||
};
|
||||
typedef struct Boxa BOXA;
|
||||
|
||||
struct Boxaa
|
||||
{
|
||||
l_int32 n; /* number of boxa in ptr array */
|
||||
l_int32 nalloc; /* number of boxa ptrs allocated */
|
||||
struct Boxa **boxa; /* boxa ptr array */
|
||||
};
|
||||
typedef struct Boxaa BOXAA;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Array of points *
|
||||
*-------------------------------------------------------------------------*/
|
||||
#define PTA_VERSION_NUMBER 1
|
||||
|
||||
struct Pta
|
||||
{
|
||||
l_int32 n; /* actual number of pts */
|
||||
l_int32 nalloc; /* size of allocated arrays */
|
||||
l_int32 refcount; /* reference count (1 if no clones) */
|
||||
l_float32 *x, *y; /* arrays of floats */
|
||||
};
|
||||
typedef struct Pta PTA;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Array of Pta *
|
||||
*-------------------------------------------------------------------------*/
|
||||
struct Ptaa
|
||||
{
|
||||
l_int32 n; /* number of pta in ptr array */
|
||||
l_int32 nalloc; /* number of pta ptrs allocated */
|
||||
struct Pta **pta; /* pta ptr array */
|
||||
};
|
||||
typedef struct Ptaa PTAA;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Pix accumulator container *
|
||||
*-------------------------------------------------------------------------*/
|
||||
struct Pixacc
|
||||
{
|
||||
l_int32 w; /* array width */
|
||||
l_int32 h; /* array height */
|
||||
l_int32 offset; /* used to allow negative */
|
||||
/* intermediate results */
|
||||
struct Pix *pix; /* the 32 bit accumulator pix */
|
||||
};
|
||||
typedef struct Pixacc PIXACC;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Pix tiling *
|
||||
*-------------------------------------------------------------------------*/
|
||||
struct PixTiling
|
||||
{
|
||||
struct Pix *pix; /* input pix (a clone) */
|
||||
l_int32 nx; /* number of tiles horizontally */
|
||||
l_int32 ny; /* number of tiles vertically */
|
||||
l_int32 w; /* tile width */
|
||||
l_int32 h; /* tile height */
|
||||
l_int32 xoverlap; /* overlap on left and right */
|
||||
l_int32 yoverlap; /* overlap on top and bottom */
|
||||
l_int32 strip; /* strip for paint; default is TRUE */
|
||||
};
|
||||
typedef struct PixTiling PIXTILING;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* FPix: pix with float array *
|
||||
*-------------------------------------------------------------------------*/
|
||||
#define FPIX_VERSION_NUMBER 1
|
||||
|
||||
struct FPix
|
||||
{
|
||||
l_int32 w; /* width in pixels */
|
||||
l_int32 h; /* height in pixels */
|
||||
l_int32 wpl; /* 32-bit words/line */
|
||||
l_int32 refcount; /* reference count (1 if no clones) */
|
||||
l_int32 xres; /* image res (ppi) in x direction */
|
||||
/* (use 0 if unknown) */
|
||||
l_int32 yres; /* image res (ppi) in y direction */
|
||||
/* (use 0 if unknown) */
|
||||
l_float32 *data; /* the float image data */
|
||||
};
|
||||
typedef struct FPix FPIX;
|
||||
|
||||
|
||||
struct FPixa
|
||||
{
|
||||
l_int32 n; /* number of Pix in ptr array */
|
||||
l_int32 nalloc; /* number of Pix ptrs allocated */
|
||||
l_uint32 refcount; /* reference count (1 if no clones) */
|
||||
struct FPix **fpix; /* the array of ptrs to fpix */
|
||||
};
|
||||
typedef struct FPixa FPIXA;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* DPix: pix with double array *
|
||||
*-------------------------------------------------------------------------*/
|
||||
#define DPIX_VERSION_NUMBER 1
|
||||
|
||||
struct DPix
|
||||
{
|
||||
l_int32 w; /* width in pixels */
|
||||
l_int32 h; /* height in pixels */
|
||||
l_int32 wpl; /* 32-bit words/line */
|
||||
l_int32 refcount; /* reference count (1 if no clones) */
|
||||
l_int32 xres; /* image res (ppi) in x direction */
|
||||
/* (use 0 if unknown) */
|
||||
l_int32 yres; /* image res (ppi) in y direction */
|
||||
/* (use 0 if unknown) */
|
||||
l_float64 *data; /* the double image data */
|
||||
};
|
||||
typedef struct DPix DPIX;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* PixComp: compressed pix *
|
||||
*-------------------------------------------------------------------------*/
|
||||
struct PixComp
|
||||
{
|
||||
l_int32 w; /* width in pixels */
|
||||
l_int32 h; /* height in pixels */
|
||||
l_int32 d; /* depth in bits */
|
||||
l_int32 xres; /* image res (ppi) in x direction */
|
||||
/* (use 0 if unknown) */
|
||||
l_int32 yres; /* image res (ppi) in y direction */
|
||||
/* (use 0 if unknown) */
|
||||
l_int32 comptype; /* compressed format (IFF_TIFF_G4, */
|
||||
/* IFF_PNG, IFF_JFIF_JPEG) */
|
||||
char *text; /* text string associated with pix */
|
||||
l_int32 cmapflag; /* flag (1 for cmap, 0 otherwise) */
|
||||
l_uint8 *data; /* the compressed image data */
|
||||
size_t size; /* size of the data array */
|
||||
};
|
||||
typedef struct PixComp PIXC;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* PixaComp: array of compressed pix *
|
||||
*-------------------------------------------------------------------------*/
|
||||
#define PIXACOMP_VERSION_NUMBER 1
|
||||
|
||||
struct PixaComp
|
||||
{
|
||||
l_int32 n; /* number of PixComp in ptr array */
|
||||
l_int32 nalloc; /* number of PixComp ptrs allocated */
|
||||
struct PixComp **pixc; /* the array of ptrs to PixComp */
|
||||
struct Boxa *boxa; /* array of boxes */
|
||||
};
|
||||
typedef struct PixaComp PIXAC;
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Access and storage flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
/*
|
||||
* For Pix, Box, Pta and Numa, there are 3 standard methods for handling
|
||||
* the retrieval or insertion of a struct:
|
||||
* (1) direct insertion (Don't do this if there is another handle
|
||||
* somewhere to this same struct!)
|
||||
* (2) copy (Always safe, sets up a refcount of 1 on the new object.
|
||||
* Can be undesirable if very large, such as an image or
|
||||
* an array of images.)
|
||||
* (3) clone (Makes another handle to the same struct, and bumps the
|
||||
* refcount up by 1. Safe to do unless you're changing
|
||||
* data through one of the handles but don't want those
|
||||
* changes to be seen by the other handle.)
|
||||
*
|
||||
* For Pixa and Boxa, which are structs that hold an array of clonable
|
||||
* structs, there is an additional method:
|
||||
* (4) copy-clone (Makes a new higher-level struct with a refcount
|
||||
* of 1, but clones all the structs in the array.)
|
||||
*
|
||||
* Unlike the other structs, when retrieving a string from an Sarray,
|
||||
* you are allowed to get a handle without a copy or clone (i.e., that
|
||||
* you don't own!). You must not free or insert such a string!
|
||||
* Specifically, for an Sarray, the copyflag for retrieval is either:
|
||||
* TRUE (or 1 or L_COPY)
|
||||
* or
|
||||
* FALSE (or 0 or L_NOCOPY)
|
||||
* For insertion, the copyflag is either:
|
||||
* TRUE (or 1 or L_COPY)
|
||||
* or
|
||||
* FALSE (or 0 or L_INSERT)
|
||||
* Note that L_COPY is always 1, and L_INSERT and L_NOCOPY are always 0.
|
||||
*/
|
||||
enum {
|
||||
L_INSERT = 0, /* stuff it in; no copy, clone or copy-clone */
|
||||
L_COPY = 1, /* make/use a copy of the object */
|
||||
L_CLONE = 2, /* make/use clone (ref count) of the object */
|
||||
L_COPY_CLONE = 3 /* make a new object and fill with with clones */
|
||||
/* of each object in the array(s) */
|
||||
};
|
||||
static const l_int32 L_NOCOPY = 0; /* copyflag value in sarrayGetString() */
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*
|
||||
* Sort flags *
|
||||
*--------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_SORT_INCREASING = 1, /* sort in increasing order */
|
||||
L_SORT_DECREASING = 2 /* sort in decreasing order */
|
||||
};
|
||||
|
||||
enum {
|
||||
L_SORT_BY_X = 3, /* sort box or c.c. by horiz location */
|
||||
L_SORT_BY_Y = 4, /* sort box or c.c. by vert location */
|
||||
L_SORT_BY_WIDTH = 5, /* sort box or c.c. by width */
|
||||
L_SORT_BY_HEIGHT = 6, /* sort box or c.c. by height */
|
||||
L_SORT_BY_MIN_DIMENSION = 7, /* sort box or c.c. by min dimension */
|
||||
L_SORT_BY_MAX_DIMENSION = 8, /* sort box or c.c. by max dimension */
|
||||
L_SORT_BY_PERIMETER = 9, /* sort box or c.c. by perimeter */
|
||||
L_SORT_BY_AREA = 10, /* sort box or c.c. by area */
|
||||
L_SORT_BY_ASPECT_RATIO = 11 /* sort box or c.c. by width/height ratio */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Blend flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_BLEND_WITH_INVERSE = 1, /* add some of src inverse to itself */
|
||||
L_BLEND_TO_WHITE = 2, /* shift src colors towards white */
|
||||
L_BLEND_TO_BLACK = 3, /* shift src colors towards black */
|
||||
L_BLEND_GRAY = 4, /* blend src directly with blender */
|
||||
L_BLEND_GRAY_WITH_INVERSE = 5 /* add amount of src inverse to itself, */
|
||||
/* based on blender pix value */
|
||||
};
|
||||
|
||||
enum {
|
||||
L_PAINT_LIGHT = 1, /* colorize non-black pixels */
|
||||
L_PAINT_DARK = 2 /* colorize non-white pixels */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Graphics pixel setting *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_SET_PIXELS = 1, /* set all bits in each pixel to 1 */
|
||||
L_CLEAR_PIXELS = 2, /* set all bits in each pixel to 0 */
|
||||
L_FLIP_PIXELS = 3 /* flip all bits in each pixel */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Size filter flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_SELECT_WIDTH = 1, /* width must satisfy constraint */
|
||||
L_SELECT_HEIGHT = 2, /* height must satisfy constraint */
|
||||
L_SELECT_IF_EITHER = 3, /* either width or height can satisfy */
|
||||
L_SELECT_IF_BOTH = 4 /* both width and height must satisfy */
|
||||
};
|
||||
|
||||
enum {
|
||||
L_SELECT_IF_LT = 1, /* save if value is less than threshold */
|
||||
L_SELECT_IF_GT = 2, /* save if value is more than threshold */
|
||||
L_SELECT_IF_LTE = 3, /* save if value is <= to the threshold */
|
||||
L_SELECT_IF_GTE = 4 /* save if value is >= to the threshold */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Color component selection flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_SELECT_RED = 1, /* use red component */
|
||||
L_SELECT_GREEN = 2, /* use green component */
|
||||
L_SELECT_BLUE = 3, /* use blue component */
|
||||
L_SELECT_MIN = 4, /* use min color component */
|
||||
L_SELECT_MAX = 5 /* use max color component */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Rotate and shear flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_ROTATE_AREA_MAP = 1, /* use area map rotation, if possible */
|
||||
L_ROTATE_SHEAR = 2, /* use shear rotation */
|
||||
L_ROTATE_SAMPLING = 3 /* use sampling */
|
||||
};
|
||||
|
||||
enum {
|
||||
L_BRING_IN_WHITE = 1, /* bring in white pixels from the outside */
|
||||
L_BRING_IN_BLACK = 2 /* bring in black pixels from the outside */
|
||||
};
|
||||
|
||||
enum {
|
||||
L_SHEAR_ABOUT_CORNER = 1, /* shear image about UL corner */
|
||||
L_SHEAR_ABOUT_CENTER = 2 /* shear image about center */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Affine transform order flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_TR_SC_RO = 1, /* translate, scale, rotate */
|
||||
L_SC_RO_TR = 2, /* scale, rotate, translate */
|
||||
L_RO_TR_SC = 3, /* rotate, translate, scale */
|
||||
L_TR_RO_SC = 4, /* translate, rotate, scale */
|
||||
L_RO_SC_TR = 5, /* rotate, scale, translate */
|
||||
L_SC_TR_RO = 6 /* scale, translate, rotate */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Grayscale filling flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_FILL_WHITE = 1, /* fill white pixels (e.g, in fg map) */
|
||||
L_FILL_BLACK = 2 /* fill black pixels (e.g., in bg map) */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Flags for setting to white or black *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_SET_WHITE = 1, /* set pixels to white */
|
||||
L_SET_BLACK = 2 /* set pixels to black */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Dither parameters *
|
||||
* If within this grayscale distance from black or white, *
|
||||
* do not propagate excess or deficit to neighboring pixels. *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
DEFAULT_CLIP_LOWER_1 = 10, /* dist to black with no prop; 1 bpp */
|
||||
DEFAULT_CLIP_UPPER_1 = 10, /* dist to black with no prop; 1 bpp */
|
||||
DEFAULT_CLIP_LOWER_2 = 5, /* dist to black with no prop; 2 bpp */
|
||||
DEFAULT_CLIP_UPPER_2 = 5 /* dist to black with no prop; 2 bpp */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Distance flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_MANHATTAN_DISTANCE = 1, /* L1 distance (e.g., in color space) */
|
||||
L_EUCLIDEAN_DISTANCE = 2 /* L2 distance */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Statistical measures *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_MEAN_ABSVAL = 1, /* average of abs values */
|
||||
L_MEDIAN_VAL = 2, /* median value of set */
|
||||
L_MODE_VAL = 3, /* mode value of set */
|
||||
L_MODE_COUNT = 4, /* mode count of set */
|
||||
L_ROOT_MEAN_SQUARE = 5, /* rms of values */
|
||||
L_STANDARD_DEVIATION = 6, /* standard deviation from mean */
|
||||
L_VARIANCE = 7 /* variance of values */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Set selection flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_CHOOSE_CONSECUTIVE = 1, /* select 'n' consecutive */
|
||||
L_CHOOSE_SKIP_BY = 2 /* select at intervals of 'n' */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Text orientation flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_TEXT_ORIENT_UNKNOWN = 0, /* low confidence on text orientation */
|
||||
L_TEXT_ORIENT_UP = 1, /* portrait, text rightside-up */
|
||||
L_TEXT_ORIENT_LEFT = 2, /* landscape, text up to left */
|
||||
L_TEXT_ORIENT_DOWN = 3, /* portrait, text upside-down */
|
||||
L_TEXT_ORIENT_RIGHT = 4 /* landscape, text up to right */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Edge orientation flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_HORIZONTAL_EDGES = 0, /* filters for horizontal edges */
|
||||
L_VERTICAL_EDGES = 1, /* filters for vertical edges */
|
||||
L_ALL_EDGES = 2 /* filters for all edges */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Line orientation flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_HORIZONTAL_LINE = 0, /* horizontal line */
|
||||
L_POS_SLOPE_LINE = 1, /* 45 degree line with positive slope */
|
||||
L_VERTICAL_LINE = 2, /* vertical line */
|
||||
L_NEG_SLOPE_LINE = 3, /* 45 degree line with negative slope */
|
||||
L_OBLIQUE_LINE = 4 /* neither horizontal nor vertical */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Scan direction flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_FROM_LEFT = 0, /* scan from left */
|
||||
L_FROM_RIGHT = 1, /* scan from right */
|
||||
L_FROM_TOP = 2, /* scan from top */
|
||||
L_FROM_BOTTOM = 3 /* scan from bottom */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Horizontal warp *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_WARP_TO_LEFT = 1, /* increasing stretch or contraction to left */
|
||||
L_WARP_TO_RIGHT = 2 /* increasing stretch or contraction to right */
|
||||
};
|
||||
|
||||
enum {
|
||||
L_LINEAR_WARP = 1, /* stretch or contraction grows linearly */
|
||||
L_QUADRATIC_WARP = 2 /* stretch or contraction grows quadratically */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Pixel selection for resampling *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_INTERPOLATED = 1, /* linear interpolation from src pixels */
|
||||
L_SAMPLED = 2 /* nearest src pixel sampling only */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Thinning flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_THIN_FG = 1, /* thin foreground of 1 bpp image */
|
||||
L_THIN_BG = 2 /* thin background of 1 bpp image */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Runlength flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_HORIZONTAL_RUNS = 0, /* determine runlengths of horizontal runs */
|
||||
L_VERTICAL_RUNS = 1 /* determine runlengths of vertical runs */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Edge filter flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_SOBEL_EDGE = 1, /* Sobel edge filter */
|
||||
L_TWO_SIDED_EDGE = 2 /* Two-sided edge filter */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Handling negative values in conversion to unsigned int *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_CLIP_TO_ZERO = 1, /* Clip negative values to 0 */
|
||||
L_TAKE_ABSVAL = 2 /* Convert to positive using L_ABS() */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Subpixel color component ordering in LCD display *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_SUBPIXEL_ORDER_RGB = 1, /* sensor order left-to-right RGB */
|
||||
L_SUBPIXEL_ORDER_BGR = 2, /* sensor order left-to-right BGR */
|
||||
L_SUBPIXEL_ORDER_VRGB = 3, /* sensor order top-to-bottom RGB */
|
||||
L_SUBPIXEL_ORDER_VBGR = 4 /* sensor order top-to-bottom BGR */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Relative to zero flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_LESS_THAN_ZERO = 1, /* Choose values less than zero */
|
||||
L_EQUAL_TO_ZERO = 2, /* Choose values equal to zero */
|
||||
L_GREATER_THAN_ZERO = 3 /* Choose values greater than zero */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* HSV histogram flags *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_HS_HISTO = 1, /* Use hue-saturation histogram */
|
||||
L_HV_HISTO = 2, /* Use hue-value histogram */
|
||||
L_SV_HISTO = 3 /* Use saturation-value histogram */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Region flags (inclusion, exclusion) *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_INCLUDE_REGION = 1, /* Use hue-saturation histogram */
|
||||
L_EXCLUDE_REGION = 2 /* Use hue-value histogram */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Flags for adding text to a pix *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_ADD_ABOVE = 1, /* Add text above the image */
|
||||
L_ADD_AT_TOP = 2, /* Add text over the top of the image */
|
||||
L_ADD_AT_BOTTOM = 3, /* Add text over the bottom of the image */
|
||||
L_ADD_BELOW = 4 /* Add text below the image */
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Flags for selecting display program *
|
||||
*-------------------------------------------------------------------------*/
|
||||
enum {
|
||||
L_DISPLAY_WITH_XV = 1, /* Use xv with pixDisplay() */
|
||||
L_DISPLAY_WITH_XLI = 2, /* Use xli with pixDisplay() */
|
||||
L_DISPLAY_WITH_XZGV = 3, /* Use xzgv with pixDisplay() */
|
||||
L_DISPLAY_WITH_IV = 4 /* Use irfvanview with pixDisplay() */
|
||||
};
|
||||
|
||||
#endif /* LEPTONICA_PIX_H */
|
|
@ -1,290 +0,0 @@
|
|||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
// Author: rays@google.com (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 TESSERACT_TRAINING_TRAININGSAMPLESET_H__
|
||||
#define TESSERACT_TRAINING_TRAININGSAMPLESET_H__
|
||||
|
||||
#include "bitvector.h"
|
||||
#include "genericvector.h"
|
||||
#include "indexmapbidi.h"
|
||||
#include "matrix.h"
|
||||
#include "shapetable.h"
|
||||
#include "trainingsample.h"
|
||||
|
||||
class UNICHARSET;
|
||||
template <typename T> class UnicityTable;
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
struct FontInfo;
|
||||
class IntFeatureMap;
|
||||
class IntFeatureSpace;
|
||||
class TrainingSample;
|
||||
class UnicharAndFonts;
|
||||
|
||||
// Collection of TrainingSample used for training or testing a classifier.
|
||||
// Provides several useful methods to operate on the collection as a whole,
|
||||
// including outlier detection and deletion, providing access by font and
|
||||
// class, finding the canonical sample, finding the "cloud" features (OR of
|
||||
// all features in all samples), replication of samples, caching of distance
|
||||
// metrics.
|
||||
class TrainingSampleSet {
|
||||
public:
|
||||
explicit TrainingSampleSet(const UnicityTable<FontInfo>& fontinfo_table);
|
||||
~TrainingSampleSet();
|
||||
|
||||
// Writes to the given file. Returns false in case of error.
|
||||
bool Serialize(FILE* fp) const;
|
||||
// Reads from the given file. Returns false in case of error.
|
||||
// If swap is true, assumes a big/little-endian swap is needed.
|
||||
bool DeSerialize(bool swap, FILE* fp);
|
||||
|
||||
// Accessors
|
||||
int num_samples() const {
|
||||
return samples_.size();
|
||||
}
|
||||
int num_raw_samples() const {
|
||||
return num_raw_samples_;
|
||||
}
|
||||
int NumFonts() const {
|
||||
return font_id_map_.SparseSize();
|
||||
}
|
||||
const UNICHARSET& unicharset() const {
|
||||
return unicharset_;
|
||||
}
|
||||
int charsetsize() const {
|
||||
return unicharset_size_;
|
||||
}
|
||||
|
||||
// Loads an initial unicharset, or sets one up if the file cannot be read.
|
||||
void LoadUnicharset(const char* filename);
|
||||
|
||||
// Adds a character sample to this sample set.
|
||||
// If the unichar is not already in the local unicharset, it is added.
|
||||
// Returns the unichar_id of the added sample, from the local unicharset.
|
||||
int AddSample(const char* unichar, TrainingSample* sample);
|
||||
// Adds a character sample to this sample set with the given unichar_id,
|
||||
// which must correspond to the local unicharset (in this).
|
||||
void AddSample(int unichar_id, TrainingSample* sample);
|
||||
|
||||
// Returns the number of samples for the given font,class pair.
|
||||
// If randomize is true, returns the number of samples accessible
|
||||
// with randomizing on. (Increases the number of samples if small.)
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
int NumClassSamples(int font_id, int class_id, bool randomize) const;
|
||||
|
||||
// Gets a sample by its index.
|
||||
const TrainingSample* GetSample(int index) const;
|
||||
|
||||
// Gets a sample by its font, class, index.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
const TrainingSample* GetSample(int font_id, int class_id, int index) const;
|
||||
|
||||
// Get a sample by its font, class, index. Does not randomize.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
TrainingSample* MutableSample(int font_id, int class_id, int index);
|
||||
|
||||
// Returns a string debug representation of the given sample:
|
||||
// font, unichar_str, bounding box, page.
|
||||
STRING SampleToString(const TrainingSample& sample) const;
|
||||
|
||||
// Gets the combined set of features used by all the samples of the given
|
||||
// font/class combination.
|
||||
const BitVector& GetCloudFeatures(int font_id, int class_id) const;
|
||||
// Gets the indexed features of the canonical sample of the given
|
||||
// font/class combination.
|
||||
const GenericVector<int>& GetCanonicalFeatures(int font_id,
|
||||
int class_id) const;
|
||||
|
||||
// Returns the distance between the given UniCharAndFonts pair.
|
||||
// If matched_fonts, only matching fonts, are considered, unless that yields
|
||||
// the empty set.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
float UnicharDistance(const UnicharAndFonts& uf1, const UnicharAndFonts& uf2,
|
||||
bool matched_fonts, const IntFeatureMap& feature_map);
|
||||
|
||||
// Returns the distance between the given pair of font/class pairs.
|
||||
// Finds in cache or computes and caches.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
float ClusterDistance(int font_id1, int class_id1,
|
||||
int font_id2, int class_id2,
|
||||
const IntFeatureMap& feature_map);
|
||||
|
||||
// Computes the distance between the given pair of font/class pairs.
|
||||
float ComputeClusterDistance(int font_id1, int class_id1,
|
||||
int font_id2, int class_id2,
|
||||
const IntFeatureMap& feature_map) const;
|
||||
|
||||
// Returns the number of canonical features of font/class 2 for which
|
||||
// neither the feature nor any of its near neighbors occurs in the cloud
|
||||
// of font/class 1. Each such feature is a reliable separation between
|
||||
// the classes, ASSUMING that the canonical sample is sufficiently
|
||||
// representative that every sample has a feature near that particular
|
||||
// feature. To check that this is so on the fly would be prohibitively
|
||||
// expensive, but it might be possible to pre-qualify the canonical features
|
||||
// to include only those for which this assumption is true.
|
||||
// ComputeCanonicalFeatures and ComputeCloudFeatures must have been called
|
||||
// first, or the results will be nonsense.
|
||||
int ReliablySeparable(int font_id1, int class_id1,
|
||||
int font_id2, int class_id2,
|
||||
const IntFeatureMap& feature_map,
|
||||
bool thorough) const;
|
||||
|
||||
|
||||
// Returns the total index of the requested sample.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
int GlobalSampleIndex(int font_id, int class_id, int index) const;
|
||||
|
||||
// Gets the canonical sample for the given font, class pair.
|
||||
// ComputeCanonicalSamples must have been called first.
|
||||
const TrainingSample* GetCanonicalSample(int font_id, int class_id) const;
|
||||
// Gets the max distance for the given canonical sample.
|
||||
// ComputeCanonicalSamples must have been called first.
|
||||
float GetCanonicalDist(int font_id, int class_id) const;
|
||||
|
||||
// Returns a mutable pointer to the sample with the given index.
|
||||
TrainingSample* mutable_sample(int index) {
|
||||
return samples_[index];
|
||||
}
|
||||
// Gets ownership of the sample with the given index, removing it from this.
|
||||
TrainingSample* extract_sample(int index) {
|
||||
TrainingSample* sample = samples_[index];
|
||||
samples_[index] = NULL;
|
||||
return sample;
|
||||
}
|
||||
|
||||
// Generates indexed features for all samples with the supplied feature_space.
|
||||
void IndexFeatures(const IntFeatureSpace& feature_space);
|
||||
|
||||
// Delete outlier samples with few features that are shared with others.
|
||||
// IndexFeatures must have been called already.
|
||||
void DeleteOutliers(const IntFeatureSpace& feature_space, bool debug);
|
||||
|
||||
// Marks the given sample for deletion.
|
||||
// Deletion is actually completed by DeleteDeadSamples.
|
||||
void KillSample(TrainingSample* sample);
|
||||
|
||||
// Deletes all samples with a negative sample index marked by KillSample.
|
||||
// Must be called before OrganizeByFontAndClass, and OrganizeByFontAndClass
|
||||
// must be called after as the samples have been renumbered.
|
||||
void DeleteDeadSamples();
|
||||
|
||||
// Callback function returns true if the given sample is to be deleted, due
|
||||
// to having a negative classid.
|
||||
bool DeleteableSample(const TrainingSample* sample);
|
||||
|
||||
// Construct an array to access the samples by font,class pair.
|
||||
void OrganizeByFontAndClass();
|
||||
|
||||
// Constructs the font_id_map_ which maps real font_ids (sparse) to a compact
|
||||
// index for the font_class_array_.
|
||||
void SetupFontIdMap();
|
||||
|
||||
// Finds the sample for each font, class pair that has least maximum
|
||||
// distance to all the other samples of the same font, class.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
void ComputeCanonicalSamples(const IntFeatureMap& map, bool debug);
|
||||
|
||||
// Replicates the samples to a minimum frequency defined by
|
||||
// 2 * kSampleRandomSize, or for larger counts duplicates all samples.
|
||||
// After replication, the replicated samples are perturbed slightly, but
|
||||
// in a predictable and repeatable way.
|
||||
// Use after OrganizeByFontAndClass().
|
||||
void ReplicateAndRandomizeSamples();
|
||||
|
||||
// Caches the indexed features of the canonical samples.
|
||||
// ComputeCanonicalSamples must have been already called.
|
||||
void ComputeCanonicalFeatures();
|
||||
// Computes the combined set of features used by all the samples of each
|
||||
// font/class combination. Use after ReplicateAndRandomizeSamples.
|
||||
void ComputeCloudFeatures(int feature_space_size);
|
||||
|
||||
// Adds all fonts of the given class to the shape.
|
||||
void AddAllFontsForClass(int class_id, Shape* shape) const;
|
||||
|
||||
// Display the samples with the given indexed feature that also match
|
||||
// the given shape.
|
||||
void DisplaySamplesWithFeature(int f_index, const Shape& shape,
|
||||
const IntFeatureSpace& feature_space,
|
||||
ScrollView::Color color,
|
||||
ScrollView* window) const;
|
||||
|
||||
private:
|
||||
// Struct to store a triplet of unichar, font, distance in the distance cache.
|
||||
struct FontClassDistance {
|
||||
int unichar_id;
|
||||
int font_id; // Real font id.
|
||||
float distance;
|
||||
};
|
||||
// Simple struct to store information related to each font/class combination.
|
||||
struct FontClassInfo {
|
||||
FontClassInfo();
|
||||
|
||||
// Writes to the given file. Returns false in case of error.
|
||||
bool Serialize(FILE* fp) const;
|
||||
// Reads from the given file. Returns false in case of error.
|
||||
// If swap is true, assumes a big/little-endian swap is needed.
|
||||
bool DeSerialize(bool swap, FILE* fp);
|
||||
|
||||
// Number of raw samples.
|
||||
inT32 num_raw_samples;
|
||||
// Index of the canonical sample.
|
||||
inT32 canonical_sample;
|
||||
// Max distance of the canonical sample from any other.
|
||||
float canonical_dist;
|
||||
// Sample indices for the samples, including replicated.
|
||||
GenericVector<inT32> samples;
|
||||
|
||||
// Non-serialized cache data.
|
||||
// Indexed features of the canonical sample.
|
||||
GenericVector<int> canonical_features;
|
||||
// The mapped features of all the samples.
|
||||
BitVector cloud_features;
|
||||
|
||||
// Caches for ClusterDistance.
|
||||
// Caches for other fonts but matching this unichar. -1 indicates not set.
|
||||
// Indexed by compact font index from font_id_map_.
|
||||
GenericVector<float> font_distance_cache;
|
||||
// Caches for other unichars but matching this font. -1 indicates not set.
|
||||
GenericVector<float> unichar_distance_cache;
|
||||
// Cache for the rest (non matching font and unichar.)
|
||||
// A cache of distances computed by ReliablySeparable.
|
||||
GenericVector<FontClassDistance> distance_cache;
|
||||
};
|
||||
|
||||
PointerVector<TrainingSample> samples_;
|
||||
// Number of samples before replication/randomization.
|
||||
int num_raw_samples_;
|
||||
// Character set we are training for.
|
||||
UNICHARSET unicharset_;
|
||||
// Character set size to which the 2-d arrays below refer.
|
||||
int unicharset_size_;
|
||||
// Map to allow the font_class_array_ below to be compact.
|
||||
// The sparse space is the real font_id, used in samples_ .
|
||||
// The compact space is an index to font_class_array_
|
||||
IndexMapBiDi font_id_map_;
|
||||
// A 2-d array of FontClassInfo holding information related to each
|
||||
// (font_id, class_id) pair.
|
||||
GENERIC_2D_ARRAY<FontClassInfo>* font_class_array_;
|
||||
|
||||
// Reference to the fontinfo_table_ in MasterTrainer. Provides names
|
||||
// for font_ids in the samples. Not serialized!
|
||||
const UnicityTable<FontInfo>& fontinfo_table_;
|
||||
};
|
||||
|
||||
} // namespace tesseract.
|
||||
|
||||
|
||||
#endif // TRAININGSAMPLESETSET_H_
|
|
@ -1,135 +0,0 @@
|
|||
/* -*-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 <math.h>
|
||||
|
||||
/*
|
||||
----------------------------------------------------------------------
|
||||
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
|
|
@ -1,62 +0,0 @@
|
|||
.highlight .hll { background-color: #ffffcc }
|
||||
.highlight { background: #eeffcc; }
|
||||
.highlight .c { color: #408090; font-style: italic } /* Comment */
|
||||
.highlight .err { border: 1px solid #FF0000 } /* Error */
|
||||
.highlight .k { color: #007020; font-weight: bold } /* Keyword */
|
||||
.highlight .o { color: #666666 } /* Operator */
|
||||
.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp { color: #007020 } /* Comment.Preproc */
|
||||
.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
|
||||
.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
|
||||
.highlight .gd { color: #A00000 } /* Generic.Deleted */
|
||||
.highlight .ge { font-style: italic } /* Generic.Emph */
|
||||
.highlight .gr { color: #FF0000 } /* Generic.Error */
|
||||
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.highlight .gi { color: #00A000 } /* Generic.Inserted */
|
||||
.highlight .go { color: #303030 } /* Generic.Output */
|
||||
.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
|
||||
.highlight .gs { font-weight: bold } /* Generic.Strong */
|
||||
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.highlight .gt { color: #0040D0 } /* Generic.Traceback */
|
||||
.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
|
||||
.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
|
||||
.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
|
||||
.highlight .kp { color: #007020 } /* Keyword.Pseudo */
|
||||
.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
|
||||
.highlight .kt { color: #902000 } /* Keyword.Type */
|
||||
.highlight .m { color: #208050 } /* Literal.Number */
|
||||
.highlight .s { color: #4070a0 } /* Literal.String */
|
||||
.highlight .na { color: #4070a0 } /* Name.Attribute */
|
||||
.highlight .nb { color: #007020 } /* Name.Builtin */
|
||||
.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
|
||||
.highlight .no { color: #60add5 } /* Name.Constant */
|
||||
.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
|
||||
.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
|
||||
.highlight .ne { color: #007020 } /* Name.Exception */
|
||||
.highlight .nf { color: #06287e } /* Name.Function */
|
||||
.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
|
||||
.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
|
||||
.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
|
||||
.highlight .nv { color: #bb60d5 } /* Name.Variable */
|
||||
.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
|
||||
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.highlight .mf { color: #208050 } /* Literal.Number.Float */
|
||||
.highlight .mh { color: #208050 } /* Literal.Number.Hex */
|
||||
.highlight .mi { color: #208050 } /* Literal.Number.Integer */
|
||||
.highlight .mo { color: #208050 } /* Literal.Number.Oct */
|
||||
.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
|
||||
.highlight .sc { color: #4070a0 } /* Literal.String.Char */
|
||||
.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
|
||||
.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
|
||||
.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
|
||||
.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
|
||||
.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
|
||||
.highlight .sx { color: #c65d09 } /* Literal.String.Other */
|
||||
.highlight .sr { color: #235388 } /* Literal.String.Regex */
|
||||
.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
|
||||
.highlight .ss { color: #517918 } /* Literal.String.Symbol */
|
||||
.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
|
||||
.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
|
||||
.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
|
||||
.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
|
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 363 B |
|
@ -1,426 +0,0 @@
|
|||
/* -*-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 <malloc.h> or <stdlib.h>
|
||||
*
|
||||
* 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_LIST'.
|
||||
All of these routines check for the NIL_LIST 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_node to access the head name = (char *) first_node (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_node to access the head name = (char *) first_node (l);
|
||||
|
||||
To implement LISP like functions use:
|
||||
|
||||
first_node CAR x = (int) first_node (l);
|
||||
rest CDR l = list_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_node (push (a, b))
|
||||
b = list_rest (push (a, b))
|
||||
a = push (pop (a), a)) For all a <> NIL_LIST
|
||||
a = reverse (reverse (a))
|
||||
|
||||
******************************************************************************/
|
||||
#include "oldlist.h"
|
||||
#include "structures.h"
|
||||
#include <stdio.h>
|
||||
#if MAC_OR_DOS
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#include "freelist.h"
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
M a c r o s
|
||||
----------------------------------------------------------------------*/
|
||||
#define add_on(l,x) l = push (l,first_node (x))
|
||||
#define next_one(l) l = list_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;
|
||||
LIST last_one = NIL_LIST;
|
||||
|
||||
if (is_equal == NULL)
|
||||
is_equal = is_same;
|
||||
|
||||
while (list != NIL_LIST) {
|
||||
if (!(*is_equal) (first_node (list), key)) {
|
||||
if (last_one == NIL_LIST) {
|
||||
last_one = list;
|
||||
list = list_rest (list);
|
||||
result = last_one;
|
||||
set_rest(last_one, NIL_LIST);
|
||||
}
|
||||
else {
|
||||
set_rest(last_one, list);
|
||||
last_one = list;
|
||||
list = list_rest (list);
|
||||
set_rest(last_one, NIL_LIST);
|
||||
}
|
||||
}
|
||||
else {
|
||||
list = pop (list);
|
||||
}
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
LIST delete_d(LIST list, void *key,
|
||||
TessResultCallback2<int, void*, void*>* is_equal) {
|
||||
LIST result = NIL_LIST;
|
||||
LIST last_one = NIL_LIST;
|
||||
|
||||
while (list != NIL_LIST) {
|
||||
if (!(*is_equal).Run (first_node (list), key)) {
|
||||
if (last_one == NIL_LIST) {
|
||||
last_one = list;
|
||||
list = list_rest (list);
|
||||
result = last_one;
|
||||
set_rest(last_one, NIL_LIST);
|
||||
}
|
||||
else {
|
||||
set_rest(last_one, list);
|
||||
last_one = list;
|
||||
list = list_rest (list);
|
||||
set_rest(last_one, NIL_LIST);
|
||||
}
|
||||
}
|
||||
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_LIST) {
|
||||
next = list_rest (list);
|
||||
free_cell(list);
|
||||
list = next;
|
||||
}
|
||||
return (NIL_LIST);
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 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_LIST) {
|
||||
(*destructor) (first_node (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_LIST) {
|
||||
element = push (NIL_LIST, node);
|
||||
set_rest (element, list_rest (list));
|
||||
set_rest(list, element);
|
||||
node = first_node (list);
|
||||
list->node = first_node (list_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_LIST)
|
||||
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 (list_rest (var_list) != NIL_LIST)
|
||||
var_list = 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 = list_rest (list);
|
||||
|
||||
if (list != NIL_LIST) {
|
||||
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_LIST) {
|
||||
t = last (list);
|
||||
t->next = push (NIL_LIST, item);
|
||||
return (list);
|
||||
}
|
||||
else
|
||||
return (push (NIL_LIST, 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_LIST;
|
||||
|
||||
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_node (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_LIST 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_node (list), key))
|
||||
return (list);
|
||||
return (NIL_LIST);
|
||||
}
|
||||
|
||||
LIST search(LIST list, void *key, TessResultCallback2<int, void*, void*>* is_equal) {
|
||||
iterate (list) if ((*is_equal).Run(first_node (list), key))
|
||||
return (list);
|
||||
return (NIL_LIST);
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: char_samp_enum.cpp
|
||||
* Description: Implementation of a Character Sample Enumerator Class
|
||||
* Author: Ahmad Abdulkader
|
||||
* Created: 2007
|
||||
*
|
||||
* (C) Copyright 2008, 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 "char_samp_enum.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
CharSampEnum::CharSampEnum() {
|
||||
}
|
||||
|
||||
CharSampEnum::~CharSampEnum() {
|
||||
}
|
||||
|
||||
} // namespace ocrlib
|
|
@ -1,85 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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"
|
||||
|
||||
struct Pix;
|
||||
|
||||
class C_BLOB:public ELIST_LINK
|
||||
{
|
||||
public:
|
||||
C_BLOB() {
|
||||
}
|
||||
explicit C_BLOB(C_OUTLINE_LIST *outline_list);
|
||||
// Simpler constructor to build a blob from a single outline that has
|
||||
// already been fully initialized.
|
||||
explicit C_BLOB(C_OUTLINE* outline);
|
||||
|
||||
// Build and return a fake blob containing a single fake outline with no
|
||||
// steps.
|
||||
static C_BLOB* FakeBlob(const TBOX& box);
|
||||
|
||||
C_OUTLINE_LIST *out_list() { //get outline list
|
||||
return &outlines;
|
||||
}
|
||||
|
||||
TBOX bounding_box(); //compute bounding box
|
||||
inT32 area(); //compute area
|
||||
inT32 perimeter(); // Total perimeter of outlines and 1st level children.
|
||||
inT32 outer_area(); //compute area
|
||||
inT32 count_transitions( //count maxima
|
||||
inT32 threshold); //size threshold
|
||||
|
||||
void move(const ICOORD vec); // repostion blob by vector
|
||||
void rotate(const FCOORD& rotation); // Rotate by given vector.
|
||||
|
||||
// Returns a Pix rendering of the blob. pixDestroy after use.
|
||||
Pix* render();
|
||||
// Returns a Pix rendering of the outline of the blob. (no fill).
|
||||
// pixDestroy after use.
|
||||
Pix* render_outline();
|
||||
|
||||
void plot( //draw one
|
||||
ScrollView* window, //window to draw in
|
||||
ScrollView::Color blob_colour, //for outer bits
|
||||
ScrollView::Color child_colour); //for holes
|
||||
|
||||
C_BLOB& operator= (const C_BLOB & source) {
|
||||
if (!outlines.empty ())
|
||||
outlines.clear();
|
||||
outlines.deep_copy(&source.outlines, &C_OUTLINE::deep_copy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static C_BLOB* deep_copy(const C_BLOB* src) {
|
||||
C_BLOB* blob = new C_BLOB;
|
||||
*blob = *src;
|
||||
return blob;
|
||||
}
|
||||
|
||||
private:
|
||||
C_OUTLINE_LIST outlines; //master elements
|
||||
};
|
||||
|
||||
ELISTIZEH (C_BLOB)
|
||||
#endif
|
|
@ -1,285 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: con_comp.cpp
|
||||
* Description: Implementation of a Connected Component class
|
||||
* Author: Ahmad Abdulkader
|
||||
* Created: 2007
|
||||
*
|
||||
* (C) Copyright 2008, 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "con_comp.h"
|
||||
#include "cube_const.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
ConComp::ConComp() {
|
||||
head_ = NULL;
|
||||
tail_ = NULL;
|
||||
left_ = 0;
|
||||
top_ = 0;
|
||||
right_ = 0;
|
||||
bottom_ = 0;
|
||||
left_most_ = false;
|
||||
right_most_ = false;
|
||||
id_ = -1;
|
||||
pt_cnt_ = 0;
|
||||
}
|
||||
|
||||
ConComp::~ConComp() {
|
||||
if (head_ != NULL) {
|
||||
ConCompPt *pt_ptr = head_;
|
||||
while (pt_ptr != NULL) {
|
||||
ConCompPt *pptNext = pt_ptr->Next();
|
||||
delete pt_ptr;
|
||||
pt_ptr = pptNext;
|
||||
}
|
||||
head_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// adds a pt to the conn comp and updates its boundaries
|
||||
bool ConComp::Add(int x, int y) {
|
||||
ConCompPt *pt_ptr = new ConCompPt(x, y);
|
||||
if (pt_ptr == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (head_ == NULL) {
|
||||
left_ = x;
|
||||
right_ = x;
|
||||
top_ = y;
|
||||
bottom_ = y;
|
||||
|
||||
head_ = pt_ptr;
|
||||
} else {
|
||||
left_ = left_ <= x ? left_ : x;
|
||||
top_ = top_ <= y ? top_ : y;
|
||||
right_ = right_ >= x ? right_ : x;
|
||||
bottom_ = bottom_ >= y ? bottom_ : y;
|
||||
}
|
||||
|
||||
if (tail_ != NULL) {
|
||||
tail_->SetNext(pt_ptr);
|
||||
}
|
||||
|
||||
tail_ = pt_ptr;
|
||||
pt_cnt_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// merges two connected components
|
||||
bool ConComp::Merge(ConComp *concomp) {
|
||||
if (head_ == NULL || tail_ == NULL ||
|
||||
concomp->head_ == NULL || concomp->tail_ == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tail_->SetNext(concomp->head_);
|
||||
tail_ = concomp->tail_;
|
||||
left_ = left_ <= concomp->left_ ? left_ : concomp->left_;
|
||||
top_ = top_ <= concomp->top_ ? top_ : concomp->top_;
|
||||
right_ = right_ >= concomp->right_ ? right_ : concomp->right_;
|
||||
bottom_ = bottom_ >= concomp->bottom_ ? bottom_ : concomp->bottom_;
|
||||
pt_cnt_ += concomp->pt_cnt_;
|
||||
|
||||
concomp->head_ = NULL;
|
||||
concomp->tail_ = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Creates the x-coord density histogram after spreading
|
||||
// each x-coord position by the HIST_WND_RATIO fraction of the
|
||||
// height of the ConComp, but limited to max_hist_wnd
|
||||
int *ConComp::CreateHistogram(int max_hist_wnd) {
|
||||
int wid = right_ - left_ + 1,
|
||||
hgt = bottom_ - top_ + 1,
|
||||
hist_wnd = static_cast<int>(hgt * HIST_WND_RATIO);
|
||||
|
||||
if (hist_wnd > max_hist_wnd) {
|
||||
hist_wnd = max_hist_wnd;
|
||||
}
|
||||
|
||||
// alloc memo for histogram
|
||||
int *hist_array = new int[wid];
|
||||
if (hist_array == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(hist_array, 0, wid * sizeof(*hist_array));
|
||||
|
||||
// compute windowed histogram
|
||||
ConCompPt *pt_ptr = head_;
|
||||
|
||||
while (pt_ptr != NULL) {
|
||||
int x = pt_ptr->x() - left_,
|
||||
xw = x - hist_wnd;
|
||||
|
||||
for (int xdel = -hist_wnd; xdel <= hist_wnd; xdel++, xw++) {
|
||||
if (xw >= 0 && xw < wid) {
|
||||
hist_array[xw]++;
|
||||
}
|
||||
}
|
||||
|
||||
pt_ptr = pt_ptr->Next();
|
||||
}
|
||||
|
||||
return hist_array;
|
||||
}
|
||||
|
||||
// find out the seg pts by looking for local minima in the histogram
|
||||
int *ConComp::SegmentHistogram(int *hist_array, int *seg_pt_cnt) {
|
||||
// init
|
||||
(*seg_pt_cnt) = 0;
|
||||
|
||||
int wid = right_ - left_ + 1,
|
||||
hgt = bottom_ - top_ + 1;
|
||||
|
||||
int *x_seg_pt = new int[wid];
|
||||
if (x_seg_pt == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int seg_pt_wnd = static_cast<int>(hgt * SEG_PT_WND_RATIO);
|
||||
|
||||
if (seg_pt_wnd > 1) {
|
||||
seg_pt_wnd = 1;
|
||||
}
|
||||
|
||||
for (int x = 2; x < (wid - 2); x++) {
|
||||
if (hist_array[x] < hist_array[x - 1] &&
|
||||
hist_array[x] < hist_array[x - 2] &&
|
||||
hist_array[x] <= hist_array[x + 1] &&
|
||||
hist_array[x] <= hist_array[x + 2]) {
|
||||
x_seg_pt[(*seg_pt_cnt)++] = x;
|
||||
x += seg_pt_wnd;
|
||||
} else if (hist_array[x] <= hist_array[x - 1] &&
|
||||
hist_array[x] <= hist_array[x - 2] &&
|
||||
hist_array[x] < hist_array[x + 1] &&
|
||||
hist_array[x] < hist_array[x + 2]) {
|
||||
x_seg_pt[(*seg_pt_cnt)++] = x;
|
||||
x += seg_pt_wnd;
|
||||
}
|
||||
}
|
||||
|
||||
// no segments, nothing to do
|
||||
if ((*seg_pt_cnt) == 0) {
|
||||
delete []x_seg_pt;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return x_seg_pt;
|
||||
}
|
||||
|
||||
// segments a concomp based on pixel density histogram local minima
|
||||
// if there were none found, it returns NULL
|
||||
// this is more useful than creating a clone of itself
|
||||
ConComp **ConComp::Segment(int max_hist_wnd, int *concomp_cnt) {
|
||||
// init
|
||||
(*concomp_cnt) = 0;
|
||||
|
||||
// No pts
|
||||
if (head_ == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int seg_pt_cnt = 0;
|
||||
|
||||
// create the histogram
|
||||
int *hist_array = CreateHistogram(max_hist_wnd);
|
||||
if (hist_array == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int *x_seg_pt = SegmentHistogram(hist_array, &seg_pt_cnt);
|
||||
|
||||
// free histogram
|
||||
delete []hist_array;
|
||||
|
||||
// no segments, nothing to do
|
||||
if (seg_pt_cnt == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// create concomp array
|
||||
ConComp **concomp_array = new ConComp *[seg_pt_cnt + 1];
|
||||
if (concomp_array == NULL) {
|
||||
delete []x_seg_pt;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int concomp = 0; concomp <= seg_pt_cnt; concomp++) {
|
||||
concomp_array[concomp] = new ConComp();
|
||||
if (concomp_array[concomp] == NULL) {
|
||||
delete []x_seg_pt;
|
||||
delete []concomp_array;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// split concomps inherit the ID this concomp
|
||||
concomp_array[concomp]->SetID(id_);
|
||||
}
|
||||
|
||||
// set the left and right most attributes of the
|
||||
// appropriate concomps
|
||||
concomp_array[0]->left_most_ = true;
|
||||
concomp_array[seg_pt_cnt]->right_most_ = true;
|
||||
|
||||
// assign pts to concomps
|
||||
ConCompPt *pt_ptr = head_;
|
||||
while (pt_ptr != NULL) {
|
||||
int seg_pt;
|
||||
|
||||
// find the first seg-pt that exceeds the x value
|
||||
// of the pt
|
||||
for (seg_pt = 0; seg_pt < seg_pt_cnt; seg_pt++) {
|
||||
if ((x_seg_pt[seg_pt] + left_) > pt_ptr->x()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add the pt to the proper concomp
|
||||
if (concomp_array[seg_pt]->Add(pt_ptr->x(), pt_ptr->y()) == false) {
|
||||
delete []x_seg_pt;
|
||||
delete []concomp_array;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pt_ptr = pt_ptr->Next();
|
||||
}
|
||||
|
||||
delete []x_seg_pt;
|
||||
|
||||
(*concomp_cnt) = (seg_pt_cnt + 1);
|
||||
|
||||
return concomp_array;
|
||||
}
|
||||
|
||||
// Shifts the co-ordinates of all points by the specified x & y deltas
|
||||
void ConComp::Shift(int dx, int dy) {
|
||||
ConCompPt *pt_ptr = head_;
|
||||
|
||||
while (pt_ptr != NULL) {
|
||||
pt_ptr->Shift(dx, dy);
|
||||
pt_ptr = pt_ptr->Next();
|
||||
}
|
||||
|
||||
left_ += dx;
|
||||
right_ += dx;
|
||||
top_ += dy;
|
||||
bottom_ += dy;
|
||||
}
|
||||
|
||||
} // namespace tesseract
|
|
@ -1,162 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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 <stdio.h>
|
||||
|
||||
#include "quspline.h"
|
||||
#include "werd.h"
|
||||
|
||||
class TO_ROW;
|
||||
|
||||
class PARA;
|
||||
|
||||
class ROW:public ELIST_LINK
|
||||
{
|
||||
friend void tweak_row_baseline(ROW *, double, double);
|
||||
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;
|
||||
}
|
||||
void set_x_height(float new_xheight) { // set x height
|
||||
xheight = new_xheight;
|
||||
}
|
||||
inT32 kern() const { //return kerning
|
||||
return kerning;
|
||||
}
|
||||
float body_size() const { //return body size
|
||||
return bodysize;
|
||||
}
|
||||
void set_body_size(float new_size) { // set body size
|
||||
bodysize = new_size;
|
||||
}
|
||||
inT32 space() const { //return spacing
|
||||
return spacing;
|
||||
}
|
||||
float ascenders() const { //return size
|
||||
return ascrise;
|
||||
}
|
||||
float descenders() const { //return size
|
||||
return descdrop;
|
||||
}
|
||||
TBOX bounding_box() const { //return bounding box
|
||||
return bound_box;
|
||||
}
|
||||
|
||||
void set_lmargin(inT16 lmargin) {
|
||||
lmargin_ = lmargin;
|
||||
}
|
||||
void set_rmargin(inT16 rmargin) {
|
||||
rmargin_ = rmargin;
|
||||
}
|
||||
inT16 lmargin() const {
|
||||
return lmargin_;
|
||||
}
|
||||
inT16 rmargin() const {
|
||||
return rmargin_;
|
||||
}
|
||||
|
||||
void set_has_drop_cap(bool has) {
|
||||
has_drop_cap_ = has;
|
||||
}
|
||||
bool has_drop_cap() const {
|
||||
return has_drop_cap_;
|
||||
}
|
||||
|
||||
void set_para(PARA *p) {
|
||||
para_ = p;
|
||||
}
|
||||
PARA *para() const {
|
||||
return para_;
|
||||
}
|
||||
|
||||
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
|
||||
ScrollView* window, //window to draw in
|
||||
ScrollView::Color colour); //uniform colour
|
||||
void plot( //draw one
|
||||
ScrollView* window); //in rainbow colours
|
||||
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
void plot_baseline( //draw the baseline
|
||||
ScrollView* window, //window to draw in
|
||||
ScrollView::Color colour) { //colour to draw
|
||||
//draw it
|
||||
baseline.plot (window, colour);
|
||||
}
|
||||
#endif
|
||||
ROW& operator= (const ROW & source);
|
||||
|
||||
private:
|
||||
inT32 kerning; //inter char gap
|
||||
inT32 spacing; //inter word gap
|
||||
TBOX bound_box; //bounding box
|
||||
float xheight; //height of line
|
||||
float ascrise; //size of ascenders
|
||||
float descdrop; //-size of descenders
|
||||
float bodysize; //CJK character size. (equals to
|
||||
//xheight+ascrise by default)
|
||||
WERD_LIST words; //words
|
||||
QSPLINE baseline; //baseline spline
|
||||
|
||||
// These get set after blocks have been determined.
|
||||
bool has_drop_cap_;
|
||||
inT16 lmargin_; // Distance to left polyblock margin.
|
||||
inT16 rmargin_; // Distance to right polyblock margin.
|
||||
|
||||
// This gets set during paragraph analysis.
|
||||
PARA *para_; // Paragraph of which this row is part.
|
||||
};
|
||||
|
||||
ELISTIZEH (ROW)
|
||||
#endif
|
|
@ -1,954 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////
|
||||
// File: bbgrid.h
|
||||
// Description: Class to hold BLOBNBOXs in a grid for fast access
|
||||
// to neighbours.
|
||||
// Author: Ray Smith
|
||||
// Created: Wed Jun 06 17:22:01 PDT 2007
|
||||
//
|
||||
// (C) Copyright 2007, 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 TESSERACT_TEXTORD_BBGRID_H__
|
||||
#define TESSERACT_TEXTORD_BBGRID_H__
|
||||
|
||||
#include "clst.h"
|
||||
#include "coutln.h"
|
||||
#include "rect.h"
|
||||
#include "scrollview.h"
|
||||
|
||||
// Some code is dependent upon leptonica. If you don't have it,
|
||||
// you don't get this functionality.
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config_auto.h"
|
||||
#endif
|
||||
|
||||
#include "allheaders.h"
|
||||
|
||||
class BLOCK;
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
// Helper function to return a scaled Pix with one pixel per grid cell,
|
||||
// set (black) where the given outline enters the corresponding grid cell,
|
||||
// and clear where the outline does not touch the grid cell.
|
||||
// Also returns the grid coords of the bottom-left of the Pix, in *left
|
||||
// and *bottom, which corresponds to (0, 0) on the Pix.
|
||||
// Note that the Pix is used upside-down, with (0, 0) being the bottom-left.
|
||||
Pix* TraceOutlineOnReducedPix(C_OUTLINE* outline, int gridsize,
|
||||
ICOORD bleft, int* left, int* bottom);
|
||||
// As TraceOutlineOnReducedPix above, but on a BLOCK instead of a C_OUTLINE.
|
||||
Pix* TraceBlockOnReducedPix(BLOCK* block, int gridsize,
|
||||
ICOORD bleft, int* left, int* bottom);
|
||||
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT> class GridSearch;
|
||||
|
||||
// The GridBase class is the base class for BBGrid and IntGrid.
|
||||
// It holds the geometry and scale of the grid.
|
||||
class GridBase {
|
||||
public:
|
||||
GridBase();
|
||||
GridBase(int gridsize, const ICOORD& bleft, const ICOORD& tright);
|
||||
virtual ~GridBase();
|
||||
|
||||
// (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
|
||||
// and bleft, tright are the bounding box of everything to go in it.
|
||||
void Init(int gridsize, const ICOORD& bleft, const ICOORD& tright);
|
||||
|
||||
// Simple accessors.
|
||||
int gridsize() const {
|
||||
return gridsize_;
|
||||
}
|
||||
int gridwidth() const {
|
||||
return gridwidth_;
|
||||
}
|
||||
int gridheight() const {
|
||||
return gridheight_;
|
||||
}
|
||||
const ICOORD& bleft() const {
|
||||
return bleft_;
|
||||
}
|
||||
const ICOORD& tright() const {
|
||||
return tright_;
|
||||
}
|
||||
// Compute the given grid coordinates from image coords.
|
||||
void GridCoords(int x, int y, int* grid_x, int* grid_y) const;
|
||||
|
||||
// Clip the given grid coordinates to fit within the grid.
|
||||
void ClipGridCoords(int* x, int* y) const;
|
||||
|
||||
protected:
|
||||
// TODO(rays) Make these private and migrate to the accessors in subclasses.
|
||||
int gridsize_; // Pixel size of each grid cell.
|
||||
int gridwidth_; // Size of the grid in cells.
|
||||
int gridheight_;
|
||||
int gridbuckets_; // Total cells in grid.
|
||||
ICOORD bleft_; // Pixel coords of bottom-left of grid.
|
||||
ICOORD tright_; // Pixel coords of top-right of grid.
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
// The IntGrid maintains a single int for each cell in a grid.
|
||||
class IntGrid : public GridBase {
|
||||
public:
|
||||
IntGrid();
|
||||
IntGrid(int gridsize, const ICOORD& bleft, const ICOORD& tright);
|
||||
virtual ~IntGrid();
|
||||
|
||||
// (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
|
||||
// and bleft, tright are the bounding box of everything to go in it.
|
||||
void Init(int gridsize, const ICOORD& bleft, const ICOORD& tright);
|
||||
|
||||
// Clear all the ints in the grid to zero.
|
||||
void Clear();
|
||||
|
||||
// Rotate the grid by rotation, keeping cell contents.
|
||||
// rotation must be a multiple of 90 degrees.
|
||||
// NOTE: due to partial cells, cell coverage in the rotated grid will be
|
||||
// inexact. This is why there is no Rotate for the generic BBGrid.
|
||||
void Rotate(const FCOORD& rotation);
|
||||
|
||||
// Returns a new IntGrid containing values equal to the sum of all the
|
||||
// neighbouring cells. The returned grid must be deleted after use.
|
||||
IntGrid* NeighbourhoodSum() const;
|
||||
|
||||
int GridCellValue(int grid_x, int grid_y) const {
|
||||
ClipGridCoords(&grid_x, &grid_y);
|
||||
return grid_[grid_y * gridwidth_ + grid_x];
|
||||
}
|
||||
void SetGridCell(int grid_x, int grid_y, int value) {
|
||||
ASSERT_HOST(grid_x >= 0 && grid_x < gridwidth());
|
||||
ASSERT_HOST(grid_y >= 0 && grid_y < gridheight());
|
||||
grid_[grid_y * gridwidth_ + grid_x] = value;
|
||||
}
|
||||
// Returns true if more than half the area of the rect is covered by grid
|
||||
// cells that are over the theshold.
|
||||
bool RectMostlyOverThreshold(const TBOX& rect, int threshold) const;
|
||||
|
||||
// Returns true if any cell value in the given rectangle is zero.
|
||||
bool AnyZeroInRect(const TBOX& rect) const;
|
||||
|
||||
// Returns a full-resolution binary pix in which each cell over the given
|
||||
// threshold is filled as a black square. pixDestroy after use.
|
||||
Pix* ThresholdToPix(int threshold) const;
|
||||
|
||||
private:
|
||||
int* grid_; // 2-d array of ints.
|
||||
};
|
||||
|
||||
// The BBGrid class holds C_LISTs of template classes BBC (bounding box class)
|
||||
// in a grid for fast neighbour access.
|
||||
// The BBC class must have a member const TBOX& bounding_box() const.
|
||||
// The BBC class must have been CLISTIZEH'ed elsewhere to make the
|
||||
// list class BBC_CLIST and the iterator BBC_C_IT.
|
||||
// Use of C_LISTs enables BBCs to exist in multiple cells simultaneously.
|
||||
// As a consequence, ownership of BBCs is assumed to be elsewhere and
|
||||
// persistent for at least the life of the BBGrid, or at least until Clear is
|
||||
// called which removes all references to inserted objects without actually
|
||||
// deleting them.
|
||||
// Most uses derive a class from a specific instantiation of BBGrid,
|
||||
// thereby making most of the ugly template notation go away.
|
||||
// The friend class GridSearch, with the same template arguments, is
|
||||
// used to search a grid efficiently in one of several search patterns.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT> class BBGrid
|
||||
: public GridBase {
|
||||
friend class GridSearch<BBC, BBC_CLIST, BBC_C_IT>;
|
||||
public:
|
||||
BBGrid();
|
||||
BBGrid(int gridsize, const ICOORD& bleft, const ICOORD& tright);
|
||||
virtual ~BBGrid();
|
||||
|
||||
// (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
|
||||
// and bleft, tright are the bounding box of everything to go in it.
|
||||
void Init(int gridsize, const ICOORD& bleft, const ICOORD& tright);
|
||||
|
||||
// Empty all the lists but leave the grid itself intact.
|
||||
void Clear();
|
||||
// Deallocate the data in the lists but otherwise leave the lists and the grid
|
||||
// intact.
|
||||
void ClearGridData(void (*free_method)(BBC*));
|
||||
|
||||
// Insert a bbox into the appropriate place in the grid.
|
||||
// If h_spread, then all cells covered horizontally by the box are
|
||||
// used, otherwise, just the bottom-left. Similarly for v_spread.
|
||||
// WARNING: InsertBBox may invalidate an active GridSearch. Call
|
||||
// RepositionIterator() on any GridSearches that are active on this grid.
|
||||
void InsertBBox(bool h_spread, bool v_spread, BBC* bbox);
|
||||
|
||||
// Using a pix from TraceOutlineOnReducedPix or TraceBlockOnReducedPix, in
|
||||
// which each pixel corresponds to a grid cell, insert a bbox into every
|
||||
// place in the grid where the corresponding pixel is 1. The Pix is handled
|
||||
// upside-down to match the Tesseract coordinate system. (As created by
|
||||
// TraceOutlineOnReducedPix or TraceBlockOnReducedPix.)
|
||||
// (0, 0) in the pix corresponds to (left, bottom) in the
|
||||
// grid (in grid coords), and the pix works up the grid from there.
|
||||
// WARNING: InsertPixPtBBox may invalidate an active GridSearch. Call
|
||||
// RepositionIterator() on any GridSearches that are active on this grid.
|
||||
void InsertPixPtBBox(int left, int bottom, Pix* pix, BBC* bbox);
|
||||
|
||||
// Remove the bbox from the grid.
|
||||
// WARNING: Any GridSearch operating on this grid could be invalidated!
|
||||
// If a GridSearch is operating, call GridSearch::RemoveBBox() instead.
|
||||
void RemoveBBox(BBC* bbox);
|
||||
|
||||
// Returns true if the given rectangle has no overlapping elements.
|
||||
bool RectangleEmpty(const TBOX& rect);
|
||||
|
||||
// Returns an IntGrid showing the number of elements in each cell.
|
||||
// Returned IntGrid must be deleted after use.
|
||||
IntGrid* CountCellElements();
|
||||
|
||||
// Make a window of an appropriate size to display things in the grid.
|
||||
ScrollView* MakeWindow(int x, int y, const char* window_name);
|
||||
|
||||
// Display the bounding boxes of the BLOBNBOXes in this grid.
|
||||
// Use of this function requires an additional member of the BBC class:
|
||||
// ScrollView::Color BBC::BoxColor() const.
|
||||
void DisplayBoxes(ScrollView* window);
|
||||
|
||||
// ASSERT_HOST that every cell contains no more than one copy of each entry.
|
||||
void AssertNoDuplicates();
|
||||
|
||||
// Handle a click event in a display window.
|
||||
virtual void HandleClick(int x, int y);
|
||||
|
||||
protected:
|
||||
BBC_CLIST* grid_; // 2-d array of CLISTS of BBC elements.
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
// The GridSearch class enables neighbourhood searching on a BBGrid.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT> class GridSearch {
|
||||
public:
|
||||
GridSearch(BBGrid<BBC, BBC_CLIST, BBC_C_IT>* grid)
|
||||
: grid_(grid), unique_mode_(false),
|
||||
previous_return_(NULL), next_return_(NULL) {
|
||||
}
|
||||
|
||||
// Get the grid x, y coords of the most recently returned BBC.
|
||||
int GridX() const {
|
||||
return x_;
|
||||
}
|
||||
int GridY() const {
|
||||
return y_;
|
||||
}
|
||||
|
||||
// Sets the search mode to return a box only once.
|
||||
// Efficiency warning: Implementation currently uses a squared-order
|
||||
// search in the number of returned elements. Use only where a small
|
||||
// number of elements are spread over a wide area, eg ColPartitions.
|
||||
void SetUniqueMode(bool mode) {
|
||||
unique_mode_ = mode;
|
||||
}
|
||||
// TODO(rays) Replace calls to ReturnedSeedElement with SetUniqueMode.
|
||||
// It only works if the search includes the bottom-left corner.
|
||||
// Apart from full search, all other searches return a box several
|
||||
// times if the box is inserted with h_spread or v_spread.
|
||||
// This method will return true for only one occurrence of each box
|
||||
// that was inserted with both h_spread and v_spread as true.
|
||||
// It will usually return false for boxes that were not inserted with
|
||||
// both h_spread=true and v_spread=true
|
||||
bool ReturnedSeedElement() const {
|
||||
TBOX box = previous_return_->bounding_box();
|
||||
int x_center = (box.left()+box.right())/2;
|
||||
int y_center = (box.top()+box.bottom())/2;
|
||||
int grid_x, grid_y;
|
||||
grid_->GridCoords(x_center, y_center, &grid_x, &grid_y);
|
||||
return (x_ == grid_x) && (y_ == grid_y);
|
||||
}
|
||||
|
||||
// Various searching iterations... Note that these iterations
|
||||
// all share data members, so you can't run more than one iteration
|
||||
// in parallel in a single GridSearch instance, but multiple instances
|
||||
// can search the same BBGrid in parallel.
|
||||
// Note that all the searches can return blobs that may not exactly
|
||||
// match the search conditions, since they return everything in the
|
||||
// covered grid cells. It is up to the caller to check for
|
||||
// appropriateness.
|
||||
// TODO(rays) NextRectSearch only returns valid elements. Make the other
|
||||
// searches test before return also and remove the tests from code
|
||||
// that uses GridSearch.
|
||||
|
||||
// Start a new full search. Will iterate all stored blobs, from the top.
|
||||
// If the blobs have been inserted using InsertBBox, (not InsertPixPtBBox)
|
||||
// then the full search guarantees to return each blob in the grid once.
|
||||
// Other searches may return a blob more than once if they have been
|
||||
// inserted using h_spread or v_spread.
|
||||
void StartFullSearch();
|
||||
// Return the next bbox in the search or NULL if done.
|
||||
BBC* NextFullSearch();
|
||||
|
||||
// Start a new radius search. Will search in a spiral upto a
|
||||
// given maximum radius in grid cells from the given center in pixels.
|
||||
void StartRadSearch(int x, int y, int max_radius);
|
||||
// Return the next bbox in the radius search or NULL if the
|
||||
// maximum radius has been reached.
|
||||
BBC* NextRadSearch();
|
||||
|
||||
// Start a new left or right-looking search. Will search to the side
|
||||
// for a box that vertically overlaps the given vertical line segment.
|
||||
// CAVEAT: This search returns all blobs from the cells to the side
|
||||
// of the start, and somewhat below, since there is no guarantee
|
||||
// that there may not be a taller object in a lower cell. The
|
||||
// blobs returned will include all those that vertically overlap and
|
||||
// are no more than twice as high, but may also include some that do
|
||||
// not overlap and some that are more than twice as high.
|
||||
void StartSideSearch(int x, int ymin, int ymax);
|
||||
// Return the next bbox in the side search or NULL if the
|
||||
// edge has been reached. Searches left to right or right to left
|
||||
// according to the flag.
|
||||
BBC* NextSideSearch(bool right_to_left);
|
||||
|
||||
// Start a vertical-looking search. Will search up or down
|
||||
// for a box that horizontally overlaps the given line segment.
|
||||
void StartVerticalSearch(int xmin, int xmax, int y);
|
||||
// Return the next bbox in the vertical search or NULL if the
|
||||
// edge has been reached. Searches top to bottom or bottom to top
|
||||
// according to the flag.
|
||||
BBC* NextVerticalSearch(bool top_to_bottom);
|
||||
|
||||
// Start a rectangular search. Will search for a box that overlaps the
|
||||
// given rectangle.
|
||||
void StartRectSearch(const TBOX& rect);
|
||||
// Return the next bbox in the rectangular search or NULL if complete.
|
||||
BBC* NextRectSearch();
|
||||
|
||||
// Remove the last returned BBC. Will not invalidate this. May invalidate
|
||||
// any other concurrent GridSearch on the same grid. If any others are
|
||||
// in use, call RepositionIterator on those, to continue without harm.
|
||||
void RemoveBBox();
|
||||
void RepositionIterator();
|
||||
|
||||
private:
|
||||
// Factored out helper to start a search.
|
||||
void CommonStart(int x, int y);
|
||||
// Factored out helper to complete a next search.
|
||||
BBC* CommonNext();
|
||||
// Factored out final return when search is exhausted.
|
||||
BBC* CommonEnd();
|
||||
// Factored out function to set the iterator to the current x_, y_
|
||||
// grid coords and mark the cycle pt.
|
||||
void SetIterator();
|
||||
|
||||
private:
|
||||
// The grid we are searching.
|
||||
BBGrid<BBC, BBC_CLIST, BBC_C_IT>* grid_;
|
||||
// For executing a search. The different search algorithms use these in
|
||||
// different ways, but most use x_origin_ and y_origin_ as the start position.
|
||||
int x_origin_;
|
||||
int y_origin_;
|
||||
int max_radius_;
|
||||
int radius_;
|
||||
int rad_index_;
|
||||
int rad_dir_;
|
||||
TBOX rect_;
|
||||
int x_; // The current location in grid coords, of the current search.
|
||||
int y_;
|
||||
bool unique_mode_;
|
||||
BBC* previous_return_; // Previous return from Next*.
|
||||
BBC* next_return_; // Current value of it_.data() used for repositioning.
|
||||
// An iterator over the list at (x_, y_) in the grid_.
|
||||
BBC_C_IT it_;
|
||||
// List of unique returned elements used when unique_mode_ is true.
|
||||
BBC_CLIST returns_;
|
||||
};
|
||||
|
||||
// Sort function to sort a BBC by bounding_box().left().
|
||||
template<class BBC>
|
||||
int SortByBoxLeft(const void* void1, const void* void2) {
|
||||
// The void*s are actually doubly indirected, so get rid of one level.
|
||||
const BBC* p1 = *reinterpret_cast<const BBC* const *>(void1);
|
||||
const BBC* p2 = *reinterpret_cast<const BBC* const *>(void2);
|
||||
int result = p1->bounding_box().left() - p2->bounding_box().left();
|
||||
if (result != 0)
|
||||
return result;
|
||||
result = p1->bounding_box().right() - p2->bounding_box().right();
|
||||
if (result != 0)
|
||||
return result;
|
||||
result = p1->bounding_box().bottom() - p2->bounding_box().bottom();
|
||||
if (result != 0)
|
||||
return result;
|
||||
return p1->bounding_box().top() - p2->bounding_box().top();
|
||||
}
|
||||
|
||||
// Sort function to sort a BBC by bounding_box().right() in right-to-left order.
|
||||
template<class BBC>
|
||||
int SortRightToLeft(const void* void1, const void* void2) {
|
||||
// The void*s are actually doubly indirected, so get rid of one level.
|
||||
const BBC* p1 = *reinterpret_cast<const BBC* const *>(void1);
|
||||
const BBC* p2 = *reinterpret_cast<const BBC* const *>(void2);
|
||||
int result = p2->bounding_box().right() - p1->bounding_box().right();
|
||||
if (result != 0)
|
||||
return result;
|
||||
result = p2->bounding_box().left() - p1->bounding_box().left();
|
||||
if (result != 0)
|
||||
return result;
|
||||
result = p1->bounding_box().bottom() - p2->bounding_box().bottom();
|
||||
if (result != 0)
|
||||
return result;
|
||||
return p1->bounding_box().top() - p2->bounding_box().top();
|
||||
}
|
||||
|
||||
// Sort function to sort a BBC by bounding_box().bottom().
|
||||
template<class BBC>
|
||||
int SortByBoxBottom(const void* void1, const void* void2) {
|
||||
// The void*s are actually doubly indirected, so get rid of one level.
|
||||
const BBC* p1 = *reinterpret_cast<const BBC* const *>(void1);
|
||||
const BBC* p2 = *reinterpret_cast<const BBC* const *>(void2);
|
||||
int result = p1->bounding_box().bottom() - p2->bounding_box().bottom();
|
||||
if (result != 0)
|
||||
return result;
|
||||
result = p1->bounding_box().top() - p2->bounding_box().top();
|
||||
if (result != 0)
|
||||
return result;
|
||||
result = p1->bounding_box().left() - p2->bounding_box().left();
|
||||
if (result != 0)
|
||||
return result;
|
||||
return p1->bounding_box().right() - p2->bounding_box().right();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// BBGrid IMPLEMENTATION.
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
BBGrid<BBC, BBC_CLIST, BBC_C_IT>::BBGrid() : grid_(NULL) {
|
||||
}
|
||||
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
BBGrid<BBC, BBC_CLIST, BBC_C_IT>::BBGrid(
|
||||
int gridsize, const ICOORD& bleft, const ICOORD& tright)
|
||||
: grid_(NULL) {
|
||||
Init(gridsize, bleft, tright);
|
||||
}
|
||||
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
BBGrid<BBC, BBC_CLIST, BBC_C_IT>::~BBGrid() {
|
||||
if (grid_ != NULL)
|
||||
delete [] grid_;
|
||||
}
|
||||
|
||||
// (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
|
||||
// and bleft, tright are the bounding box of everything to go in it.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::Init(int gridsize,
|
||||
const ICOORD& bleft,
|
||||
const ICOORD& tright) {
|
||||
GridBase::Init(gridsize, bleft, tright);
|
||||
if (grid_ != NULL)
|
||||
delete [] grid_;
|
||||
grid_ = new BBC_CLIST[gridbuckets_];
|
||||
}
|
||||
|
||||
// Clear all lists, but leave the array of lists present.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::Clear() {
|
||||
for (int i = 0; i < gridbuckets_; ++i) {
|
||||
grid_[i].shallow_clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Deallocate the data in the lists but otherwise leave the lists and the grid
|
||||
// intact.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::ClearGridData(
|
||||
void (*free_method)(BBC*)) {
|
||||
if (grid_ == NULL) return;
|
||||
GridSearch<BBC, BBC_CLIST, BBC_C_IT> search(this);
|
||||
search.StartFullSearch();
|
||||
BBC* bb;
|
||||
BBC_CLIST bb_list;
|
||||
BBC_C_IT it(&bb_list);
|
||||
while ((bb = search.NextFullSearch()) != NULL) {
|
||||
it.add_after_then_move(bb);
|
||||
}
|
||||
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
|
||||
free_method(it.data());
|
||||
}
|
||||
}
|
||||
|
||||
// Insert a bbox into the appropriate place in the grid.
|
||||
// If h_spread, then all cells covered horizontally by the box are
|
||||
// used, otherwise, just the bottom-left. Similarly for v_spread.
|
||||
// WARNING: InsertBBox may invalidate an active GridSearch. Call
|
||||
// RepositionIterator() on any GridSearches that are active on this grid.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::InsertBBox(bool h_spread, bool v_spread,
|
||||
BBC* bbox) {
|
||||
TBOX box = bbox->bounding_box();
|
||||
int start_x, start_y, end_x, end_y;
|
||||
GridCoords(box.left(), box.bottom(), &start_x, &start_y);
|
||||
GridCoords(box.right(), box.top(), &end_x, &end_y);
|
||||
if (!h_spread)
|
||||
end_x = start_x;
|
||||
if (!v_spread)
|
||||
end_y = start_y;
|
||||
int grid_index = start_y * gridwidth_;
|
||||
for (int y = start_y; y <= end_y; ++y, grid_index += gridwidth_) {
|
||||
for (int x = start_x; x <= end_x; ++x) {
|
||||
grid_[grid_index + x].add_sorted(SortByBoxLeft<BBC>, true, bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Using a pix from TraceOutlineOnReducedPix or TraceBlockOnReducedPix, in
|
||||
// which each pixel corresponds to a grid cell, insert a bbox into every
|
||||
// place in the grid where the corresponding pixel is 1. The Pix is handled
|
||||
// upside-down to match the Tesseract coordinate system. (As created by
|
||||
// TraceOutlineOnReducedPix or TraceBlockOnReducedPix.)
|
||||
// (0, 0) in the pix corresponds to (left, bottom) in the
|
||||
// grid (in grid coords), and the pix works up the grid from there.
|
||||
// WARNING: InsertPixPtBBox may invalidate an active GridSearch. Call
|
||||
// RepositionIterator() on any GridSearches that are active on this grid.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::InsertPixPtBBox(int left, int bottom,
|
||||
Pix* pix, BBC* bbox) {
|
||||
int width = pixGetWidth(pix);
|
||||
int height = pixGetHeight(pix);
|
||||
for (int y = 0; y < height; ++y) {
|
||||
l_uint32* data = pixGetData(pix) + y * pixGetWpl(pix);
|
||||
for (int x = 0; x < width; ++x) {
|
||||
if (GET_DATA_BIT(data, x)) {
|
||||
grid_[(bottom + y) * gridwidth_ + x + left].
|
||||
add_sorted(SortByBoxLeft<BBC>, true, bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the bbox from the grid.
|
||||
// WARNING: Any GridSearch operating on this grid could be invalidated!
|
||||
// If a GridSearch is operating, call GridSearch::RemoveBBox() instead.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::RemoveBBox(BBC* bbox) {
|
||||
TBOX box = bbox->bounding_box();
|
||||
int start_x, start_y, end_x, end_y;
|
||||
GridCoords(box.left(), box.bottom(), &start_x, &start_y);
|
||||
GridCoords(box.right(), box.top(), &end_x, &end_y);
|
||||
int grid_index = start_y * gridwidth_;
|
||||
for (int y = start_y; y <= end_y; ++y, grid_index += gridwidth_) {
|
||||
for (int x = start_x; x <= end_x; ++x) {
|
||||
BBC_C_IT it(&grid_[grid_index + x]);
|
||||
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
|
||||
if (it.data() == bbox)
|
||||
it.extract();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the given rectangle has no overlapping elements.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
bool BBGrid<BBC, BBC_CLIST, BBC_C_IT>::RectangleEmpty(const TBOX& rect) {
|
||||
GridSearch<BBC, BBC_CLIST, BBC_C_IT> rsearch(this);
|
||||
rsearch.StartRectSearch(rect);
|
||||
return rsearch.NextRectSearch() == NULL;
|
||||
}
|
||||
|
||||
// Returns an IntGrid showing the number of elements in each cell.
|
||||
// Returned IntGrid must be deleted after use.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
IntGrid* BBGrid<BBC, BBC_CLIST, BBC_C_IT>::CountCellElements() {
|
||||
IntGrid* intgrid = new IntGrid(gridsize(), bleft(), tright());
|
||||
for (int y = 0; y < gridheight(); ++y) {
|
||||
for (int x = 0; x < gridwidth(); ++x) {
|
||||
int cell_count = grid_[y * gridwidth() + x].length();
|
||||
intgrid->SetGridCell(x, y, cell_count);
|
||||
}
|
||||
}
|
||||
return intgrid;
|
||||
}
|
||||
|
||||
|
||||
template<class G> class TabEventHandler : public SVEventHandler {
|
||||
public:
|
||||
explicit TabEventHandler(G* grid) : grid_(grid) {
|
||||
}
|
||||
void Notify(const SVEvent* sv_event) {
|
||||
if (sv_event->type == SVET_CLICK) {
|
||||
grid_->HandleClick(sv_event->x, sv_event->y);
|
||||
}
|
||||
}
|
||||
private:
|
||||
G* grid_;
|
||||
};
|
||||
|
||||
// Make a window of an appropriate size to display things in the grid.
|
||||
// Position the window at the given x,y.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
ScrollView* BBGrid<BBC, BBC_CLIST, BBC_C_IT>::MakeWindow(
|
||||
int x, int y, const char* window_name) {
|
||||
ScrollView* tab_win = NULL;
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
tab_win = new ScrollView(window_name, x, y,
|
||||
tright_.x() - bleft_.x(),
|
||||
tright_.y() - bleft_.y(),
|
||||
tright_.x() - bleft_.x(),
|
||||
tright_.y() - bleft_.y(),
|
||||
true);
|
||||
TabEventHandler<BBGrid<BBC, BBC_CLIST, BBC_C_IT> >* handler =
|
||||
new TabEventHandler<BBGrid<BBC, BBC_CLIST, BBC_C_IT> >(this);
|
||||
tab_win->AddEventHandler(handler);
|
||||
tab_win->Pen(ScrollView::GREY);
|
||||
tab_win->Rectangle(0, 0, tright_.x() - bleft_.x(), tright_.y() - bleft_.y());
|
||||
#endif
|
||||
return tab_win;
|
||||
}
|
||||
|
||||
// Create a window at (x,y) and display the bounding boxes of the
|
||||
// BLOBNBOXes in this grid.
|
||||
// Use of this function requires an additional member of the BBC class:
|
||||
// ScrollView::Color BBC::BoxColor() const.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::DisplayBoxes(ScrollView* tab_win) {
|
||||
#ifndef GRAPHICS_DISABLED
|
||||
tab_win->Pen(ScrollView::BLUE);
|
||||
tab_win->Brush(ScrollView::NONE);
|
||||
|
||||
// For every bbox in the grid, display it.
|
||||
GridSearch<BBC, BBC_CLIST, BBC_C_IT> gsearch(this);
|
||||
gsearch.StartFullSearch();
|
||||
BBC* bbox;
|
||||
while ((bbox = gsearch.NextFullSearch()) != NULL) {
|
||||
TBOX box = bbox->bounding_box();
|
||||
int left_x = box.left();
|
||||
int right_x = box.right();
|
||||
int top_y = box.top();
|
||||
int bottom_y = box.bottom();
|
||||
ScrollView::Color box_color = bbox->BoxColor();
|
||||
tab_win->Pen(box_color);
|
||||
tab_win->Rectangle(left_x, bottom_y, right_x, top_y);
|
||||
}
|
||||
tab_win->Update();
|
||||
#endif
|
||||
}
|
||||
|
||||
// ASSERT_HOST that every cell contains no more than one copy of each entry.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::AssertNoDuplicates() {
|
||||
// Process all grid cells.
|
||||
for (int i = gridwidth_ * gridheight_ - 1; i >= 0; --i) {
|
||||
// Iterate over all elements excent the last.
|
||||
for (BBC_C_IT it(&grid_[i]); !it.at_last(); it.forward()) {
|
||||
BBC* ptr = it.data();
|
||||
BBC_C_IT it2(it);
|
||||
// None of the rest of the elements in the list should equal ptr.
|
||||
for (it2.forward(); !it2.at_first(); it2.forward()) {
|
||||
ASSERT_HOST(it2.data() != ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle a click event in a display window.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::HandleClick(int x, int y) {
|
||||
tprintf("Click at (%d, %d)\n", x, y);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// GridSearch IMPLEMENTATION.
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Start a new full search. Will iterate all stored blobs.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartFullSearch() {
|
||||
// Full search uses x_ and y_ as the current grid
|
||||
// cell being searched.
|
||||
CommonStart(grid_->bleft_.x(), grid_->tright_.y());
|
||||
}
|
||||
|
||||
// Return the next bbox in the search or NULL if done.
|
||||
// The other searches will return a box that overlaps the grid cell
|
||||
// thereby duplicating boxes, but NextFullSearch only returns each box once.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextFullSearch() {
|
||||
int x;
|
||||
int y;
|
||||
do {
|
||||
while (it_.cycled_list()) {
|
||||
++x_;
|
||||
if (x_ >= grid_->gridwidth_) {
|
||||
--y_;
|
||||
if (y_ < 0)
|
||||
return CommonEnd();
|
||||
x_ = 0;
|
||||
}
|
||||
SetIterator();
|
||||
}
|
||||
CommonNext();
|
||||
TBOX box = previous_return_->bounding_box();
|
||||
grid_->GridCoords(box.left(), box.bottom(), &x, &y);
|
||||
} while (x != x_ || y != y_);
|
||||
return previous_return_;
|
||||
}
|
||||
|
||||
// Start a new radius search.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartRadSearch(int x, int y,
|
||||
int max_radius) {
|
||||
// Rad search uses x_origin_ and y_origin_ as the center of the circle.
|
||||
// The radius_ is the radius of the (diamond-shaped) circle and
|
||||
// rad_index/rad_dir_ combine to determine the position around it.
|
||||
max_radius_ = max_radius;
|
||||
radius_ = 0;
|
||||
rad_index_ = 0;
|
||||
rad_dir_ = 3;
|
||||
CommonStart(x, y);
|
||||
}
|
||||
|
||||
// Return the next bbox in the radius search or NULL if the
|
||||
// maximum radius has been reached.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextRadSearch() {
|
||||
do {
|
||||
while (it_.cycled_list()) {
|
||||
++rad_index_;
|
||||
if (rad_index_ >= radius_) {
|
||||
++rad_dir_;
|
||||
rad_index_ = 0;
|
||||
if (rad_dir_ >= 4) {
|
||||
++radius_;
|
||||
if (radius_ > max_radius_)
|
||||
return CommonEnd();
|
||||
rad_dir_ = 0;
|
||||
}
|
||||
}
|
||||
ICOORD offset = C_OUTLINE::chain_step(rad_dir_);
|
||||
offset *= radius_ - rad_index_;
|
||||
offset += C_OUTLINE::chain_step(rad_dir_ + 1) * rad_index_;
|
||||
x_ = x_origin_ + offset.x();
|
||||
y_ = y_origin_ + offset.y();
|
||||
if (x_ >= 0 && x_ < grid_->gridwidth_ &&
|
||||
y_ >= 0 && y_ < grid_->gridheight_)
|
||||
SetIterator();
|
||||
}
|
||||
CommonNext();
|
||||
} while (unique_mode_ &&
|
||||
!returns_.add_sorted(SortByBoxLeft<BBC>, true, previous_return_));
|
||||
return previous_return_;
|
||||
}
|
||||
|
||||
// Start a new left or right-looking search. Will search to the side
|
||||
// for a box that vertically overlaps the given vertical line segment.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartSideSearch(int x,
|
||||
int ymin, int ymax) {
|
||||
// Right search records the x in x_origin_, the ymax in y_origin_
|
||||
// and the size of the vertical strip to search in radius_.
|
||||
// To guarantee finding overlapping objects of upto twice the
|
||||
// given size, double the height.
|
||||
radius_ = ((ymax - ymin) * 2 + grid_->gridsize_ - 1) / grid_->gridsize_;
|
||||
rad_index_ = 0;
|
||||
CommonStart(x, ymax);
|
||||
}
|
||||
|
||||
// Return the next bbox in the side search or NULL if the
|
||||
// edge has been reached. Searches left to right or right to left
|
||||
// according to the flag.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextSideSearch(bool right_to_left) {
|
||||
do {
|
||||
while (it_.cycled_list()) {
|
||||
++rad_index_;
|
||||
if (rad_index_ > radius_) {
|
||||
if (right_to_left)
|
||||
--x_;
|
||||
else
|
||||
++x_;
|
||||
rad_index_ = 0;
|
||||
if (x_ < 0 || x_ >= grid_->gridwidth_)
|
||||
return CommonEnd();
|
||||
}
|
||||
y_ = y_origin_ - rad_index_;
|
||||
if (y_ >= 0 && y_ < grid_->gridheight_)
|
||||
SetIterator();
|
||||
}
|
||||
CommonNext();
|
||||
} while (unique_mode_ &&
|
||||
!returns_.add_sorted(SortByBoxLeft<BBC>, true, previous_return_));
|
||||
return previous_return_;
|
||||
}
|
||||
|
||||
// Start a vertical-looking search. Will search up or down
|
||||
// for a box that horizontally overlaps the given line segment.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartVerticalSearch(int xmin,
|
||||
int xmax,
|
||||
int y) {
|
||||
// Right search records the xmin in x_origin_, the y in y_origin_
|
||||
// and the size of the horizontal strip to search in radius_.
|
||||
radius_ = (xmax - xmin + grid_->gridsize_ - 1) / grid_->gridsize_;
|
||||
rad_index_ = 0;
|
||||
CommonStart(xmin, y);
|
||||
}
|
||||
|
||||
// Return the next bbox in the vertical search or NULL if the
|
||||
// edge has been reached. Searches top to bottom or bottom to top
|
||||
// according to the flag.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextVerticalSearch(
|
||||
bool top_to_bottom) {
|
||||
do {
|
||||
while (it_.cycled_list()) {
|
||||
++rad_index_;
|
||||
if (rad_index_ > radius_) {
|
||||
if (top_to_bottom)
|
||||
--y_;
|
||||
else
|
||||
++y_;
|
||||
rad_index_ = 0;
|
||||
if (y_ < 0 || y_ >= grid_->gridheight_)
|
||||
return CommonEnd();
|
||||
}
|
||||
x_ = x_origin_ + rad_index_;
|
||||
if (x_ >= 0 && x_ < grid_->gridwidth_)
|
||||
SetIterator();
|
||||
}
|
||||
CommonNext();
|
||||
} while (unique_mode_ &&
|
||||
!returns_.add_sorted(SortByBoxLeft<BBC>, true, previous_return_));
|
||||
return previous_return_;
|
||||
}
|
||||
|
||||
// Start a rectangular search. Will search for a box that overlaps the
|
||||
// given rectangle.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartRectSearch(const TBOX& rect) {
|
||||
// Rect search records the xmin in x_origin_, the ymin in y_origin_
|
||||
// and the xmax in max_radius_.
|
||||
// The search proceeds left to right, top to bottom.
|
||||
rect_ = rect;
|
||||
CommonStart(rect.left(), rect.top());
|
||||
grid_->GridCoords(rect.right(), rect.bottom(), // - rect.height(),
|
||||
&max_radius_, &y_origin_);
|
||||
}
|
||||
|
||||
// Return the next bbox in the rectangular search or NULL if complete.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextRectSearch() {
|
||||
do {
|
||||
while (it_.cycled_list()) {
|
||||
++x_;
|
||||
if (x_ > max_radius_) {
|
||||
--y_;
|
||||
x_ = x_origin_;
|
||||
if (y_ < y_origin_)
|
||||
return CommonEnd();
|
||||
}
|
||||
SetIterator();
|
||||
}
|
||||
CommonNext();
|
||||
} while (!rect_.overlap(previous_return_->bounding_box()) ||
|
||||
(unique_mode_ &&
|
||||
!returns_.add_sorted(SortByBoxLeft<BBC>, true, previous_return_)));
|
||||
return previous_return_;
|
||||
}
|
||||
|
||||
// Remove the last returned BBC. Will not invalidate this. May invalidate
|
||||
// any other concurrent GridSearch on the same grid. If any others are
|
||||
// in use, call RepositionIterator on those, to continue without harm.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::RemoveBBox() {
|
||||
if (previous_return_ != NULL) {
|
||||
// Remove all instances of previous_return_ from the list, so the iterator
|
||||
// remains valid after removal from the rest of the grid cells.
|
||||
// if previous_return_ is not on the list, then it has been removed already.
|
||||
BBC* prev_data = NULL;
|
||||
BBC* new_previous_return = NULL;
|
||||
it_.move_to_first();
|
||||
for (it_.mark_cycle_pt(); !it_.cycled_list();) {
|
||||
if (it_.data() == previous_return_) {
|
||||
new_previous_return = prev_data;
|
||||
it_.extract();
|
||||
it_.forward();
|
||||
next_return_ = it_.cycled_list() ? NULL : it_.data();
|
||||
} else {
|
||||
prev_data = it_.data();
|
||||
it_.forward();
|
||||
}
|
||||
}
|
||||
grid_->RemoveBBox(previous_return_);
|
||||
previous_return_ = new_previous_return;
|
||||
RepositionIterator();
|
||||
}
|
||||
}
|
||||
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::RepositionIterator() {
|
||||
// Something was deleted, so we have little choice but to clear the
|
||||
// returns list.
|
||||
returns_.shallow_clear();
|
||||
// Reset the iterator back to one past the previous return.
|
||||
// If the previous_return_ is no longer in the list, then
|
||||
// next_return_ serves as a backup.
|
||||
it_.move_to_first();
|
||||
// Special case, the first element was removed and reposition
|
||||
// iterator was called. In this case, the data is fine, but the
|
||||
// cycle point is not. Detect it and return.
|
||||
if (!it_.empty() && it_.data() == next_return_) {
|
||||
it_.mark_cycle_pt();
|
||||
return;
|
||||
}
|
||||
for (it_.mark_cycle_pt(); !it_.cycled_list(); it_.forward()) {
|
||||
if (it_.data() == previous_return_ ||
|
||||
it_.data_relative(1) == next_return_) {
|
||||
CommonNext();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// We ran off the end of the list. Move to a new cell next time.
|
||||
previous_return_ = NULL;
|
||||
next_return_ = NULL;
|
||||
}
|
||||
|
||||
// Factored out helper to start a search.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::CommonStart(int x, int y) {
|
||||
grid_->GridCoords(x, y, &x_origin_, &y_origin_);
|
||||
x_ = x_origin_;
|
||||
y_ = y_origin_;
|
||||
SetIterator();
|
||||
previous_return_ = NULL;
|
||||
next_return_ = it_.empty() ? NULL : it_.data();
|
||||
returns_.shallow_clear();
|
||||
}
|
||||
|
||||
// Factored out helper to complete a next search.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::CommonNext() {
|
||||
previous_return_ = it_.data();
|
||||
it_.forward();
|
||||
next_return_ = it_.cycled_list() ? NULL : it_.data();
|
||||
return previous_return_;
|
||||
}
|
||||
|
||||
// Factored out final return when search is exhausted.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::CommonEnd() {
|
||||
previous_return_ = NULL;
|
||||
next_return_ = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Factored out function to set the iterator to the current x_, y_
|
||||
// grid coords and mark the cycle pt.
|
||||
template<class BBC, class BBC_CLIST, class BBC_C_IT>
|
||||
void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::SetIterator() {
|
||||
it_= &(grid_->grid_[y_ * grid_->gridwidth_ + x_]);
|
||||
it_.mark_cycle_pt();
|
||||
}
|
||||
|
||||
} // namespace tesseract.
|
||||
|
||||
#endif // TESSERACT_TEXTORD_BBGRID_H__
|
|
@ -1,870 +0,0 @@
|
|||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
// Author: rays@google.com (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 "trainingsampleset.h"
|
||||
#include "allheaders.h"
|
||||
#include "boxread.h"
|
||||
#include "fontinfo.h"
|
||||
#include "indexmapbidi.h"
|
||||
#include "intfeaturedist.h"
|
||||
#include "intfeaturemap.h"
|
||||
#include "intfeaturespace.h"
|
||||
#include "shapetable.h"
|
||||
#include "trainingsample.h"
|
||||
#include "unicity_table.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
const int kTestChar = -1; // 37;
|
||||
// Max number of distances to compute the squared way
|
||||
const int kSquareLimit = 25;
|
||||
// Prime numbers for subsampling distances.
|
||||
const int kPrime1 = 17;
|
||||
const int kPrime2 = 13;
|
||||
// Min samples from which to start discarding outliers.
|
||||
const int kMinOutlierSamples = 5;
|
||||
|
||||
TrainingSampleSet::FontClassInfo::FontClassInfo()
|
||||
: num_raw_samples(0), canonical_sample(-1), canonical_dist(0.0f) {
|
||||
}
|
||||
|
||||
// Writes to the given file. Returns false in case of error.
|
||||
bool TrainingSampleSet::FontClassInfo::Serialize(FILE* fp) const {
|
||||
if (fwrite(&num_raw_samples, sizeof(num_raw_samples), 1, fp) != 1)
|
||||
return false;
|
||||
if (fwrite(&canonical_sample, sizeof(canonical_sample), 1, fp) != 1)
|
||||
return false;
|
||||
if (fwrite(&canonical_dist, sizeof(canonical_dist), 1, fp) != 1) return false;
|
||||
if (!samples.Serialize(fp)) return false;
|
||||
return true;
|
||||
}
|
||||
// Reads from the given file. Returns false in case of error.
|
||||
// If swap is true, assumes a big/little-endian swap is needed.
|
||||
bool TrainingSampleSet::FontClassInfo::DeSerialize(bool swap, FILE* fp) {
|
||||
if (fread(&num_raw_samples, sizeof(num_raw_samples), 1, fp) != 1)
|
||||
return false;
|
||||
if (fread(&canonical_sample, sizeof(canonical_sample), 1, fp) != 1)
|
||||
return false;
|
||||
if (fread(&canonical_dist, sizeof(canonical_dist), 1, fp) != 1) return false;
|
||||
if (!samples.DeSerialize(swap, fp)) return false;
|
||||
if (swap) {
|
||||
ReverseN(&num_raw_samples, sizeof(num_raw_samples));
|
||||
ReverseN(&canonical_sample, sizeof(canonical_sample));
|
||||
ReverseN(&canonical_dist, sizeof(canonical_dist));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TrainingSampleSet::TrainingSampleSet(const UnicityTable<FontInfo>& font_table)
|
||||
: num_raw_samples_(0), unicharset_size_(0),
|
||||
font_class_array_(NULL), fontinfo_table_(font_table) {
|
||||
}
|
||||
|
||||
TrainingSampleSet::~TrainingSampleSet() {
|
||||
delete font_class_array_;
|
||||
}
|
||||
|
||||
// Writes to the given file. Returns false in case of error.
|
||||
bool TrainingSampleSet::Serialize(FILE* fp) const {
|
||||
if (!samples_.Serialize(fp)) return false;
|
||||
if (!unicharset_.save_to_file(fp)) return false;
|
||||
if (!font_id_map_.Serialize(fp)) return false;
|
||||
inT8 not_null = font_class_array_ != NULL;
|
||||
if (fwrite(¬_null, sizeof(not_null), 1, fp) != 1) return false;
|
||||
if (not_null) {
|
||||
if (!font_class_array_->SerializeClasses(fp)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reads from the given file. Returns false in case of error.
|
||||
// If swap is true, assumes a big/little-endian swap is needed.
|
||||
bool TrainingSampleSet::DeSerialize(bool swap, FILE* fp) {
|
||||
if (!samples_.DeSerialize(swap, fp)) return false;
|
||||
num_raw_samples_ = samples_.size();
|
||||
if (!unicharset_.load_from_file(fp)) return false;
|
||||
if (!font_id_map_.DeSerialize(swap, fp)) return false;
|
||||
if (font_class_array_ != NULL) {
|
||||
delete font_class_array_;
|
||||
font_class_array_ = NULL;
|
||||
}
|
||||
inT8 not_null;
|
||||
if (fread(¬_null, sizeof(not_null), 1, fp) != 1) return false;
|
||||
if (not_null) {
|
||||
FontClassInfo empty;
|
||||
font_class_array_ = new GENERIC_2D_ARRAY<FontClassInfo >(1, 1 , empty);
|
||||
if (!font_class_array_->DeSerializeClasses(swap, fp)) return false;
|
||||
}
|
||||
unicharset_size_ = unicharset_.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load an initial unicharset, or set one up if the file cannot be read.
|
||||
void TrainingSampleSet::LoadUnicharset(const char* filename) {
|
||||
if (!unicharset_.load_from_file(filename)) {
|
||||
tprintf("Failed to load unicharset from file %s\n"
|
||||
"Building unicharset for boosting from scratch...\n",
|
||||
filename);
|
||||
unicharset_.clear();
|
||||
// Space character needed to represent NIL_LIST classification.
|
||||
unicharset_.unichar_insert(" ");
|
||||
}
|
||||
unicharset_size_ = unicharset_.size();
|
||||
}
|
||||
|
||||
// Adds a character sample to this sample set.
|
||||
// If the unichar is not already in the local unicharset, it is added.
|
||||
// Returns the unichar_id of the added sample, from the local unicharset.
|
||||
int TrainingSampleSet::AddSample(const char* unichar, TrainingSample* sample) {
|
||||
if (!unicharset_.contains_unichar(unichar)) {
|
||||
unicharset_.unichar_insert(unichar);
|
||||
if (unicharset_.size() > MAX_NUM_CLASSES) {
|
||||
tprintf("Error: Size of unicharset in TrainingSampleSet::AddSample is "
|
||||
"greater than MAX_NUM_CLASSES\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
UNICHAR_ID char_id = unicharset_.unichar_to_id(unichar);
|
||||
AddSample(char_id, sample);
|
||||
return char_id;
|
||||
}
|
||||
|
||||
// Adds a character sample to this sample set with the given unichar_id,
|
||||
// which must correspond to the local unicharset (in this).
|
||||
void TrainingSampleSet::AddSample(int unichar_id, TrainingSample* sample) {
|
||||
sample->set_class_id(unichar_id);
|
||||
samples_.push_back(sample);
|
||||
num_raw_samples_ = samples_.size();
|
||||
unicharset_size_ = unicharset_.size();
|
||||
}
|
||||
|
||||
// Returns the number of samples for the given font,class pair.
|
||||
// If randomize is true, returns the number of samples accessible
|
||||
// with randomizing on. (Increases the number of samples if small.)
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
int TrainingSampleSet::NumClassSamples(int font_id, int class_id,
|
||||
bool randomize) const {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
if (font_id < 0 || class_id < 0 ||
|
||||
font_id >= font_id_map_.SparseSize() || class_id >= unicharset_size_) {
|
||||
// There are no samples because the font or class doesn't exist.
|
||||
return 0;
|
||||
}
|
||||
int font_index = font_id_map_.SparseToCompact(font_id);
|
||||
if (font_index < 0)
|
||||
return 0; // The font has no samples.
|
||||
if (randomize)
|
||||
return (*font_class_array_)(font_index, class_id).samples.size();
|
||||
else
|
||||
return (*font_class_array_)(font_index, class_id).num_raw_samples;
|
||||
}
|
||||
|
||||
// Gets a sample by its index.
|
||||
const TrainingSample* TrainingSampleSet::GetSample(int index) const {
|
||||
return samples_[index];
|
||||
}
|
||||
|
||||
// Gets a sample by its font, class, index.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
const TrainingSample* TrainingSampleSet::GetSample(int font_id, int class_id,
|
||||
int index) const {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
int font_index = font_id_map_.SparseToCompact(font_id);
|
||||
if (font_index < 0) return NULL;
|
||||
int sample_index = (*font_class_array_)(font_index, class_id).samples[index];
|
||||
return samples_[sample_index];
|
||||
}
|
||||
|
||||
// Get a sample by its font, class, index. Does not randomize.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
TrainingSample* TrainingSampleSet::MutableSample(int font_id, int class_id,
|
||||
int index) {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
int font_index = font_id_map_.SparseToCompact(font_id);
|
||||
if (font_index < 0) return NULL;
|
||||
int sample_index = (*font_class_array_)(font_index, class_id).samples[index];
|
||||
return samples_[sample_index];
|
||||
}
|
||||
|
||||
// Returns a string debug representation of the given sample:
|
||||
// font, unichar_str, bounding box, page.
|
||||
STRING TrainingSampleSet::SampleToString(const TrainingSample& sample) const {
|
||||
STRING boxfile_str;
|
||||
MakeBoxFileStr(unicharset_.id_to_unichar(sample.class_id()),
|
||||
sample.bounding_box(), sample.page_num(), &boxfile_str);
|
||||
return STRING(fontinfo_table_.get(sample.font_id()).name) + " " + boxfile_str;
|
||||
}
|
||||
|
||||
// Gets the combined set of features used by all the samples of the given
|
||||
// font/class combination.
|
||||
const BitVector& TrainingSampleSet::GetCloudFeatures(
|
||||
int font_id, int class_id) const {
|
||||
int font_index = font_id_map_.SparseToCompact(font_id);
|
||||
ASSERT_HOST(font_index >= 0);
|
||||
return (*font_class_array_)(font_index, class_id).cloud_features;
|
||||
}
|
||||
// Gets the indexed features of the canonical sample of the given
|
||||
// font/class combination.
|
||||
const GenericVector<int>& TrainingSampleSet::GetCanonicalFeatures(
|
||||
int font_id, int class_id) const {
|
||||
int font_index = font_id_map_.SparseToCompact(font_id);
|
||||
ASSERT_HOST(font_index >= 0);
|
||||
return (*font_class_array_)(font_index, class_id).canonical_features;
|
||||
}
|
||||
|
||||
// Returns the distance between the given UniCharAndFonts pair.
|
||||
// If matched_fonts, only matching fonts, are considered, unless that yields
|
||||
// the empty set.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
float TrainingSampleSet::UnicharDistance(const UnicharAndFonts& uf1,
|
||||
const UnicharAndFonts& uf2,
|
||||
bool matched_fonts,
|
||||
const IntFeatureMap& feature_map) {
|
||||
int num_fonts1 = uf1.font_ids.size();
|
||||
int c1 = uf1.unichar_id;
|
||||
int num_fonts2 = uf2.font_ids.size();
|
||||
int c2 = uf2.unichar_id;
|
||||
double dist_sum = 0.0;
|
||||
int dist_count = 0;
|
||||
bool debug = false;
|
||||
if (matched_fonts) {
|
||||
// Compute distances only where fonts match.
|
||||
for (int i = 0; i < num_fonts1; ++i) {
|
||||
int f1 = uf1.font_ids[i];
|
||||
for (int j = 0; j < num_fonts2; ++j) {
|
||||
int f2 = uf2.font_ids[j];
|
||||
if (f1 == f2) {
|
||||
dist_sum += ClusterDistance(f1, c1, f2, c2, feature_map);
|
||||
++dist_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (num_fonts1 * num_fonts2 <= kSquareLimit) {
|
||||
// Small enough sets to compute all the distances.
|
||||
for (int i = 0; i < num_fonts1; ++i) {
|
||||
int f1 = uf1.font_ids[i];
|
||||
for (int j = 0; j < num_fonts2; ++j) {
|
||||
int f2 = uf2.font_ids[j];
|
||||
dist_sum += ClusterDistance(f1, c1, f2, c2, feature_map);
|
||||
if (debug) {
|
||||
tprintf("Cluster dist %d %d %d %d = %g\n",
|
||||
f1, c1, f2, c2,
|
||||
ClusterDistance(f1, c1, f2, c2, feature_map));
|
||||
}
|
||||
++dist_count;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Subsample distances, using the largest set once, and stepping through
|
||||
// the smaller set so as to ensure that all the pairs are different.
|
||||
int increment = kPrime1 != num_fonts2 ? kPrime1 : kPrime2;
|
||||
int index = 0;
|
||||
int num_samples = MAX(num_fonts1, num_fonts2);
|
||||
for (int i = 0; i < num_samples; ++i, index += increment) {
|
||||
int f1 = uf1.font_ids[i % num_fonts1];
|
||||
int f2 = uf2.font_ids[index % num_fonts2];
|
||||
if (debug) {
|
||||
tprintf("Cluster dist %d %d %d %d = %g\n",
|
||||
f1, c1, f2, c2, ClusterDistance(f1, c1, f2, c2, feature_map));
|
||||
}
|
||||
dist_sum += ClusterDistance(f1, c1, f2, c2, feature_map);
|
||||
++dist_count;
|
||||
}
|
||||
}
|
||||
if (dist_count == 0) {
|
||||
if (matched_fonts)
|
||||
return UnicharDistance(uf1, uf2, false, feature_map);
|
||||
return 0.0f;
|
||||
}
|
||||
return dist_sum / dist_count;
|
||||
}
|
||||
|
||||
// Returns the distance between the given pair of font/class pairs.
|
||||
// Finds in cache or computes and caches.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
float TrainingSampleSet::ClusterDistance(int font_id1, int class_id1,
|
||||
int font_id2, int class_id2,
|
||||
const IntFeatureMap& feature_map) {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
int font_index1 = font_id_map_.SparseToCompact(font_id1);
|
||||
int font_index2 = font_id_map_.SparseToCompact(font_id2);
|
||||
if (font_index1 < 0 || font_index2 < 0)
|
||||
return 0.0f;
|
||||
FontClassInfo& fc_info = (*font_class_array_)(font_index1, class_id1);
|
||||
if (font_id1 == font_id2) {
|
||||
// Special case cache for speed.
|
||||
if (fc_info.unichar_distance_cache.size() == 0)
|
||||
fc_info.unichar_distance_cache.init_to_size(unicharset_size_, -1.0f);
|
||||
if (fc_info.unichar_distance_cache[class_id2] < 0) {
|
||||
// Distance has to be calculated.
|
||||
float result = ComputeClusterDistance(font_id1, class_id1,
|
||||
font_id2, class_id2,
|
||||
feature_map);
|
||||
fc_info.unichar_distance_cache[class_id2] = result;
|
||||
// Copy to the symmetric cache entry.
|
||||
FontClassInfo& fc_info2 = (*font_class_array_)(font_index2, class_id2);
|
||||
if (fc_info2.unichar_distance_cache.size() == 0)
|
||||
fc_info2.unichar_distance_cache.init_to_size(unicharset_size_, -1.0f);
|
||||
fc_info2.unichar_distance_cache[class_id1] = result;
|
||||
}
|
||||
return fc_info.unichar_distance_cache[class_id2];
|
||||
} else if (class_id1 == class_id2) {
|
||||
// Another special-case cache for equal class-id.
|
||||
if (fc_info.font_distance_cache.size() == 0)
|
||||
fc_info.font_distance_cache.init_to_size(font_id_map_.CompactSize(),
|
||||
-1.0f);
|
||||
if (fc_info.font_distance_cache[font_index2] < 0) {
|
||||
// Distance has to be calculated.
|
||||
float result = ComputeClusterDistance(font_id1, class_id1,
|
||||
font_id2, class_id2,
|
||||
feature_map);
|
||||
fc_info.font_distance_cache[font_index2] = result;
|
||||
// Copy to the symmetric cache entry.
|
||||
FontClassInfo& fc_info2 = (*font_class_array_)(font_index2, class_id2);
|
||||
if (fc_info2.font_distance_cache.size() == 0)
|
||||
fc_info2.font_distance_cache.init_to_size(font_id_map_.CompactSize(),
|
||||
-1.0f);
|
||||
fc_info2.font_distance_cache[font_index1] = result;
|
||||
}
|
||||
return fc_info.font_distance_cache[font_index2];
|
||||
}
|
||||
// Both font and class are different. Linear search for class_id2/font_id2
|
||||
// in what is a hopefully short list of distances.
|
||||
int cache_index = 0;
|
||||
while (cache_index < fc_info.distance_cache.size() &&
|
||||
(fc_info.distance_cache[cache_index].unichar_id != class_id2 ||
|
||||
fc_info.distance_cache[cache_index].font_id != font_id2))
|
||||
++cache_index;
|
||||
if (cache_index == fc_info.distance_cache.size()) {
|
||||
// Distance has to be calculated.
|
||||
float result = ComputeClusterDistance(font_id1, class_id1,
|
||||
font_id2, class_id2,
|
||||
feature_map);
|
||||
FontClassDistance fc_dist = { class_id2, font_id2, result };
|
||||
fc_info.distance_cache.push_back(fc_dist);
|
||||
// Copy to the symmetric cache entry. We know it isn't there already, as
|
||||
// we always copy to the symmetric entry.
|
||||
FontClassInfo& fc_info2 = (*font_class_array_)(font_index2, class_id2);
|
||||
fc_dist.unichar_id = class_id1;
|
||||
fc_dist.font_id = font_id1;
|
||||
fc_info2.distance_cache.push_back(fc_dist);
|
||||
}
|
||||
return fc_info.distance_cache[cache_index].distance;
|
||||
}
|
||||
|
||||
// Computes the distance between the given pair of font/class pairs.
|
||||
float TrainingSampleSet::ComputeClusterDistance(
|
||||
int font_id1, int class_id1, int font_id2, int class_id2,
|
||||
const IntFeatureMap& feature_map) const {
|
||||
int dist = ReliablySeparable(font_id1, class_id1, font_id2, class_id2,
|
||||
feature_map, false);
|
||||
dist += ReliablySeparable(font_id2, class_id2, font_id1, class_id1,
|
||||
feature_map, false);
|
||||
int denominator = GetCanonicalFeatures(font_id1, class_id1).size();
|
||||
denominator += GetCanonicalFeatures(font_id2, class_id2).size();
|
||||
return static_cast<float>(dist) / denominator;
|
||||
}
|
||||
|
||||
// Helper to add a feature and its near neighbors to the good_features.
|
||||
// levels indicates how many times to compute the offset features of what is
|
||||
// already there. This is done by iteration rather than recursion.
|
||||
static void AddNearFeatures(const IntFeatureMap& feature_map, int f, int levels,
|
||||
GenericVector<int>* good_features) {
|
||||
int prev_num_features = 0;
|
||||
good_features->push_back(f);
|
||||
int num_features = 1;
|
||||
for (int level = 0; level < levels; ++level) {
|
||||
for (int i = prev_num_features; i < num_features; ++i) {
|
||||
int feature = (*good_features)[i];
|
||||
for (int dir = -kNumOffsetMaps; dir <= kNumOffsetMaps; ++dir) {
|
||||
if (dir == 0) continue;
|
||||
int f1 = feature_map.OffsetFeature(feature, dir);
|
||||
if (f1 >= 0) {
|
||||
good_features->push_back(f1);
|
||||
}
|
||||
}
|
||||
}
|
||||
prev_num_features = num_features;
|
||||
num_features = good_features->size();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the number of canonical features of font/class 2 for which
|
||||
// neither the feature nor any of its near neighbors occurs in the cloud
|
||||
// of font/class 1. Each such feature is a reliable separation between
|
||||
// the classes, ASSUMING that the canonical sample is sufficiently
|
||||
// representative that every sample has a feature near that particular
|
||||
// feature. To check that this is so on the fly would be prohibitively
|
||||
// expensive, but it might be possible to pre-qualify the canonical features
|
||||
// to include only those for which this assumption is true.
|
||||
// ComputeCanonicalFeatures and ComputeCloudFeatures must have been called
|
||||
// first, or the results will be nonsense.
|
||||
int TrainingSampleSet::ReliablySeparable(int font_id1, int class_id1,
|
||||
int font_id2, int class_id2,
|
||||
const IntFeatureMap& feature_map,
|
||||
bool thorough) const {
|
||||
int result = 0;
|
||||
const TrainingSample* sample2 = GetCanonicalSample(font_id2, class_id2);
|
||||
if (sample2 == NULL)
|
||||
return 0; // There are no canonical features.
|
||||
const GenericVector<int>& canonical2 = GetCanonicalFeatures(font_id2,
|
||||
class_id2);
|
||||
const BitVector& cloud1 = GetCloudFeatures(font_id1, class_id1);
|
||||
if (cloud1.size() == 0)
|
||||
return canonical2.size(); // There are no cloud features.
|
||||
|
||||
// Find a canonical2 feature that is not in cloud1.
|
||||
for (int f = 0; f < canonical2.size(); ++f) {
|
||||
int feature = canonical2[f];
|
||||
if (cloud1[feature])
|
||||
continue;
|
||||
// Gather the near neighbours of f.
|
||||
GenericVector<int> good_features;
|
||||
AddNearFeatures(feature_map, feature, 1, &good_features);
|
||||
// Check that none of the good_features are in the cloud.
|
||||
int i;
|
||||
for (i = 0; i < good_features.size(); ++i) {
|
||||
int good_f = good_features[i];
|
||||
if (cloud1[good_f]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < good_features.size())
|
||||
continue; // Found one in the cloud.
|
||||
++result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the total index of the requested sample.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
int TrainingSampleSet::GlobalSampleIndex(int font_id, int class_id,
|
||||
int index) const {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
int font_index = font_id_map_.SparseToCompact(font_id);
|
||||
if (font_index < 0) return -1;
|
||||
return (*font_class_array_)(font_index, class_id).samples[index];
|
||||
}
|
||||
|
||||
// Gets the canonical sample for the given font, class pair.
|
||||
// ComputeCanonicalSamples must have been called first.
|
||||
const TrainingSample* TrainingSampleSet::GetCanonicalSample(
|
||||
int font_id, int class_id) const {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
int font_index = font_id_map_.SparseToCompact(font_id);
|
||||
if (font_index < 0) return NULL;
|
||||
int sample_index = (*font_class_array_)(font_index,
|
||||
class_id).canonical_sample;
|
||||
return sample_index >= 0 ? samples_[sample_index] : NULL;
|
||||
}
|
||||
|
||||
// Gets the max distance for the given canonical sample.
|
||||
// ComputeCanonicalSamples must have been called first.
|
||||
float TrainingSampleSet::GetCanonicalDist(int font_id, int class_id) const {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
int font_index = font_id_map_.SparseToCompact(font_id);
|
||||
if (font_index < 0) return 0.0f;
|
||||
if ((*font_class_array_)(font_index, class_id).canonical_sample >= 0)
|
||||
return (*font_class_array_)(font_index, class_id).canonical_dist;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Generates indexed features for all samples with the supplied feature_space.
|
||||
void TrainingSampleSet::IndexFeatures(const IntFeatureSpace& feature_space) {
|
||||
for (int s = 0; s < samples_.size(); ++s)
|
||||
samples_[s]->IndexFeatures(feature_space);
|
||||
}
|
||||
|
||||
// Delete outlier samples with few features that are shared with others.
|
||||
// IndexFeatures must have been called already.
|
||||
void TrainingSampleSet::DeleteOutliers(const IntFeatureSpace& feature_space,
|
||||
bool debug) {
|
||||
if (font_class_array_ == NULL)
|
||||
OrganizeByFontAndClass();
|
||||
Pixa* pixa = NULL;
|
||||
if (debug)
|
||||
pixa = pixaCreate(0);
|
||||
GenericVector<int> feature_counts;
|
||||
int fs_size = feature_space.Size();
|
||||
int font_size = font_id_map_.CompactSize();
|
||||
for (int font_index = 0; font_index < font_size; ++font_index) {
|
||||
for (int c = 0; c < unicharset_size_; ++c) {
|
||||
// Create a histogram of the features used by all samples of this
|
||||
// font/class combination.
|
||||
feature_counts.init_to_size(fs_size, 0);
|
||||
FontClassInfo& fcinfo = (*font_class_array_)(font_index, c);
|
||||
int sample_count = fcinfo.samples.size();
|
||||
if (sample_count < kMinOutlierSamples)
|
||||
continue;
|
||||
for (int i = 0; i < sample_count; ++i) {
|
||||
int s = fcinfo.samples[i];
|
||||
const GenericVector<int>& features = samples_[s]->indexed_features();
|
||||
for (int f = 0; f < features.size(); ++f) {
|
||||
++feature_counts[features[f]];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < sample_count; ++i) {
|
||||
int s = fcinfo.samples[i];
|
||||
const TrainingSample& sample = *samples_[s];
|
||||
const GenericVector<int>& features = sample.indexed_features();
|
||||
// A feature that has a histogram count of 1 is only used by this
|
||||
// sample, making it 'bad'. All others are 'good'.
|
||||
int good_features = 0;
|
||||
int bad_features = 0;
|
||||
for (int f = 0; f < features.size(); ++f) {
|
||||
if (feature_counts[features[f]] > 1)
|
||||
++good_features;
|
||||
else
|
||||
++bad_features;
|
||||
}
|
||||
// If more than 1/3 features are bad, then this is an outlier.
|
||||
if (bad_features * 2 > good_features) {
|
||||
tprintf("Deleting outlier sample of %s, %d good, %d bad\n",
|
||||
SampleToString(sample).string(),
|
||||
good_features, bad_features);
|
||||
if (debug) {
|
||||
pixaAddPix(pixa, sample.RenderToPix(&unicharset_), L_INSERT);
|
||||
// Add the previous sample as well, so it is easier to see in
|
||||
// the output what is wrong with this sample.
|
||||
int t;
|
||||
if (i == 0)
|
||||
t = fcinfo.samples[1];
|
||||
else
|
||||
t = fcinfo.samples[i - 1];
|
||||
const TrainingSample &csample = *samples_[t];
|
||||
pixaAddPix(pixa, csample.RenderToPix(&unicharset_), L_INSERT);
|
||||
}
|
||||
// Mark the sample for deletion.
|
||||
KillSample(samples_[s]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Truly delete all bad samples and renumber everything.
|
||||
DeleteDeadSamples();
|
||||
if (pixa != NULL) {
|
||||
Pix* pix = pixaDisplayTiledInRows(pixa, 1, 2600, 1.0, 0, 10, 10);
|
||||
pixaDestroy(&pixa);
|
||||
pixWrite("outliers.png", pix, IFF_PNG);
|
||||
pixDestroy(&pix);
|
||||
}
|
||||
}
|
||||
|
||||
// Marks the given sample index for deletion.
|
||||
// Deletion is actually completed by DeleteDeadSamples.
|
||||
void TrainingSampleSet::KillSample(TrainingSample* sample) {
|
||||
sample->set_sample_index(-1);
|
||||
}
|
||||
|
||||
// Deletes all samples with zero features marked by KillSample.
|
||||
void TrainingSampleSet::DeleteDeadSamples() {
|
||||
samples_.compact(
|
||||
NewPermanentTessCallback(this, &TrainingSampleSet::DeleteableSample));
|
||||
num_raw_samples_ = samples_.size();
|
||||
// Samples must be re-organized now we have deleted a few.
|
||||
}
|
||||
|
||||
// Callback function returns true if the given sample is to be deleted, due
|
||||
// to having a negative classid.
|
||||
bool TrainingSampleSet::DeleteableSample(const TrainingSample* sample) {
|
||||
return sample == NULL || sample->class_id() < 0;
|
||||
}
|
||||
|
||||
static Pix* DebugSample(const UNICHARSET& unicharset,
|
||||
TrainingSample* sample) {
|
||||
tprintf("\nOriginal features:\n");
|
||||
for (int i = 0; i < sample->num_features(); ++i) {
|
||||
sample->features()[i].print();
|
||||
}
|
||||
if (sample->features_are_mapped()) {
|
||||
tprintf("\nMapped features:\n");
|
||||
for (int i = 0; i < sample->mapped_features().size(); ++i) {
|
||||
tprintf("%d ", sample->mapped_features()[i]);
|
||||
}
|
||||
tprintf("\n");
|
||||
}
|
||||
return sample->RenderToPix(&unicharset);
|
||||
}
|
||||
|
||||
// Construct an array to access the samples by font,class pair.
|
||||
void TrainingSampleSet::OrganizeByFontAndClass() {
|
||||
// Font indexes are sparse, so we used a map to compact them, so we can
|
||||
// have an efficient 2-d array of fonts and character classes.
|
||||
SetupFontIdMap();
|
||||
int compact_font_size = font_id_map_.CompactSize();
|
||||
// Get a 2-d array of generic vectors.
|
||||
if (font_class_array_ != NULL)
|
||||
delete font_class_array_;
|
||||
FontClassInfo empty;
|
||||
font_class_array_ = new GENERIC_2D_ARRAY<FontClassInfo>(
|
||||
compact_font_size, unicharset_size_, empty);
|
||||
for (int s = 0; s < samples_.size(); ++s) {
|
||||
int font_id = samples_[s]->font_id();
|
||||
int class_id = samples_[s]->class_id();
|
||||
if (font_id < 0 || font_id >= font_id_map_.SparseSize()) {
|
||||
tprintf("Font id = %d/%d, class id = %d/%d on sample %d\n",
|
||||
font_id, font_id_map_.SparseSize(), class_id, unicharset_size_,
|
||||
s);
|
||||
}
|
||||
ASSERT_HOST(font_id >= 0 && font_id < font_id_map_.SparseSize());
|
||||
ASSERT_HOST(class_id >= 0 && class_id < unicharset_size_);
|
||||
int font_index = font_id_map_.SparseToCompact(font_id);
|
||||
(*font_class_array_)(font_index, class_id).samples.push_back(s);
|
||||
}
|
||||
// Set the num_raw_samples member of the FontClassInfo, to set the boundary
|
||||
// between the raw samples and the replicated ones.
|
||||
for (int f = 0; f < compact_font_size; ++f) {
|
||||
for (int c = 0; c < unicharset_size_; ++c)
|
||||
(*font_class_array_)(f, c).num_raw_samples =
|
||||
(*font_class_array_)(f, c).samples.size();
|
||||
}
|
||||
// This is the global number of samples and also marks the boundary between
|
||||
// real and replicated samples.
|
||||
num_raw_samples_ = samples_.size();
|
||||
}
|
||||
|
||||
// Constructs the font_id_map_ which maps real font_ids (sparse) to a compact
|
||||
// index for the font_class_array_.
|
||||
void TrainingSampleSet::SetupFontIdMap() {
|
||||
// Number of samples for each font_id.
|
||||
GenericVector<int> font_counts;
|
||||
for (int s = 0; s < samples_.size(); ++s) {
|
||||
int font_id = samples_[s]->font_id();
|
||||
while (font_id >= font_counts.size())
|
||||
font_counts.push_back(0);
|
||||
++font_counts[font_id];
|
||||
}
|
||||
font_id_map_.Init(font_counts.size(), false);
|
||||
for (int f = 0; f < font_counts.size(); ++f) {
|
||||
font_id_map_.SetMap(f, font_counts[f] > 0);
|
||||
}
|
||||
font_id_map_.Setup();
|
||||
}
|
||||
|
||||
|
||||
// Finds the sample for each font, class pair that has least maximum
|
||||
// distance to all the other samples of the same font, class.
|
||||
// OrganizeByFontAndClass must have been already called.
|
||||
void TrainingSampleSet::ComputeCanonicalSamples(const IntFeatureMap& map,
|
||||
bool debug) {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
IntFeatureDist f_table;
|
||||
if (debug) tprintf("feature table size %d\n", map.sparse_size());
|
||||
f_table.Init(&map);
|
||||
int worst_s1 = 0;
|
||||
int worst_s2 = 0;
|
||||
double global_worst_dist = 0.0;
|
||||
// Compute distances independently for each font and char index.
|
||||
int font_size = font_id_map_.CompactSize();
|
||||
for (int font_index = 0; font_index < font_size; ++font_index) {
|
||||
int font_id = font_id_map_.CompactToSparse(font_index);
|
||||
for (int c = 0; c < unicharset_size_; ++c) {
|
||||
int samples_found = 0;
|
||||
FontClassInfo& fcinfo = (*font_class_array_)(font_index, c);
|
||||
if (fcinfo.samples.size() == 0 ||
|
||||
(kTestChar >= 0 && c != kTestChar)) {
|
||||
fcinfo.canonical_sample = -1;
|
||||
fcinfo.canonical_dist = 0.0f;
|
||||
if (debug) tprintf("Skipping class %d\n", c);
|
||||
continue;
|
||||
}
|
||||
// The canonical sample will be the one with the min_max_dist, which
|
||||
// is the sample with the lowest maximum distance to all other samples.
|
||||
double min_max_dist = 2.0;
|
||||
// We keep track of the farthest apart pair (max_s1, max_s2) which
|
||||
// are max_max_dist apart, so we can see how bad the variability is.
|
||||
double max_max_dist = 0.0;
|
||||
int max_s1 = 0;
|
||||
int max_s2 = 0;
|
||||
fcinfo.canonical_sample = fcinfo.samples[0];
|
||||
fcinfo.canonical_dist = 0.0f;
|
||||
for (int i = 0; i < fcinfo.samples.size(); ++i) {
|
||||
int s1 = fcinfo.samples[i];
|
||||
const GenericVector<int>& features1 = samples_[s1]->indexed_features();
|
||||
f_table.Set(features1, features1.size(), true);
|
||||
double max_dist = 0.0;
|
||||
// Run the full squared-order search for similar samples. It is still
|
||||
// reasonably fast because f_table.FeatureDistance is fast, but we
|
||||
// may have to reconsider if we start playing with too many samples
|
||||
// of a single char/font.
|
||||
for (int j = 0; j < fcinfo.samples.size(); ++j) {
|
||||
int s2 = fcinfo.samples[j];
|
||||
if (samples_[s2]->class_id() != c ||
|
||||
samples_[s2]->font_id() != font_id ||
|
||||
s2 == s1)
|
||||
continue;
|
||||
GenericVector<int> features2 = samples_[s2]->indexed_features();
|
||||
double dist = f_table.FeatureDistance(features2);
|
||||
int height = samples_[s2]->geo_feature(GeoTop) -
|
||||
samples_[s2]->geo_feature(GeoBottom);
|
||||
if (dist == 1.0 && height > 64) {
|
||||
// TODO(rays) rethink this when the polygonal approximation goes.
|
||||
// Currently it is possible for dots and other small characters
|
||||
// to be completely different, even within the same class.
|
||||
f_table.DebugFeatureDistance(features2);
|
||||
}
|
||||
if (dist > max_dist) {
|
||||
max_dist = dist;
|
||||
if (dist > max_max_dist) {
|
||||
max_s1 = s1;
|
||||
max_s2 = s2;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Using Set(..., false) is far faster than re initializing, due to
|
||||
// the sparseness of the feature space.
|
||||
f_table.Set(features1, features1.size(), false);
|
||||
samples_[s1]->set_max_dist(max_dist);
|
||||
++samples_found;
|
||||
if (max_dist < min_max_dist) {
|
||||
fcinfo.canonical_sample = s1;
|
||||
fcinfo.canonical_dist = max_dist;
|
||||
}
|
||||
UpdateRange(max_dist, &min_max_dist, &max_max_dist);
|
||||
}
|
||||
if (max_max_dist > global_worst_dist) {
|
||||
// Keep a record of the worst pair over all characters/fonts too.
|
||||
global_worst_dist = max_max_dist;
|
||||
worst_s1 = max_s1;
|
||||
worst_s2 = max_s2;
|
||||
}
|
||||
if (debug) {
|
||||
tprintf("Found %d samples of class %d=%s, font %d, "
|
||||
"dist range [%g, %g], worst pair= %s, %s\n",
|
||||
samples_found, c, unicharset_.debug_str(c).string(),
|
||||
font_index, min_max_dist, max_max_dist,
|
||||
SampleToString(*samples_[max_s1]).string(),
|
||||
SampleToString(*samples_[max_s2]).string());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (debug) {
|
||||
tprintf("Global worst dist = %g, between sample %d and %d\n",
|
||||
global_worst_dist, worst_s1, worst_s2);
|
||||
Pix* pix1 = DebugSample(unicharset_, samples_[worst_s1]);
|
||||
Pix* pix2 = DebugSample(unicharset_, samples_[worst_s2]);
|
||||
pixOr(pix1, pix1, pix2);
|
||||
pixWrite("worstpair.png", pix1, IFF_PNG);
|
||||
pixDestroy(&pix1);
|
||||
pixDestroy(&pix2);
|
||||
}
|
||||
}
|
||||
|
||||
// Replicates the samples to a minimum frequency defined by
|
||||
// 2 * kSampleRandomSize, or for larger counts duplicates all samples.
|
||||
// After replication, the replicated samples are perturbed slightly, but
|
||||
// in a predictable and repeatable way.
|
||||
// Use after OrganizeByFontAndClass().
|
||||
void TrainingSampleSet::ReplicateAndRandomizeSamples() {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
int font_size = font_id_map_.CompactSize();
|
||||
for (int font_index = 0; font_index < font_size; ++font_index) {
|
||||
for (int c = 0; c < unicharset_size_; ++c) {
|
||||
FontClassInfo& fcinfo = (*font_class_array_)(font_index, c);
|
||||
int sample_count = fcinfo.samples.size();
|
||||
int min_samples = 2 * MAX(kSampleRandomSize, sample_count);
|
||||
if (sample_count > 0 && sample_count < min_samples) {
|
||||
int base_count = sample_count;
|
||||
for (int base_index = 0; sample_count < min_samples; ++sample_count) {
|
||||
int src_index = fcinfo.samples[base_index++];
|
||||
if (base_index >= base_count) base_index = 0;
|
||||
TrainingSample* sample = samples_[src_index]->RandomizedCopy(
|
||||
sample_count % kSampleRandomSize);
|
||||
int sample_index = samples_.size();
|
||||
sample->set_sample_index(sample_index);
|
||||
samples_.push_back(sample);
|
||||
fcinfo.samples.push_back(sample_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Caches the indexed features of the canonical samples.
|
||||
// ComputeCanonicalSamples must have been already called.
|
||||
// TODO(rays) see note on ReliablySeparable and try restricting the
|
||||
// canonical features to those that truly represent all samples.
|
||||
void TrainingSampleSet::ComputeCanonicalFeatures() {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
int font_size = font_id_map_.CompactSize();
|
||||
for (int font_index = 0; font_index < font_size; ++font_index) {
|
||||
int font_id = font_id_map_.CompactToSparse(font_index);
|
||||
for (int c = 0; c < unicharset_size_; ++c) {
|
||||
int num_samples = NumClassSamples(font_id, c, false);
|
||||
if (num_samples == 0)
|
||||
continue;
|
||||
const TrainingSample* sample = GetCanonicalSample(font_id, c);
|
||||
FontClassInfo& fcinfo = (*font_class_array_)(font_index, c);
|
||||
fcinfo.canonical_features = sample->indexed_features();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Computes the combined set of features used by all the samples of each
|
||||
// font/class combination. Use after ReplicateAndRandomizeSamples.
|
||||
void TrainingSampleSet::ComputeCloudFeatures(int feature_space_size) {
|
||||
ASSERT_HOST(font_class_array_ != NULL);
|
||||
int font_size = font_id_map_.CompactSize();
|
||||
for (int font_index = 0; font_index < font_size; ++font_index) {
|
||||
int font_id = font_id_map_.CompactToSparse(font_index);
|
||||
for (int c = 0; c < unicharset_size_; ++c) {
|
||||
int num_samples = NumClassSamples(font_id, c, false);
|
||||
if (num_samples == 0)
|
||||
continue;
|
||||
FontClassInfo& fcinfo = (*font_class_array_)(font_index, c);
|
||||
fcinfo.cloud_features.Init(feature_space_size);
|
||||
for (int s = 0; s < num_samples; ++s) {
|
||||
const TrainingSample* sample = GetSample(font_id, c, s);
|
||||
const GenericVector<int>& sample_features = sample->indexed_features();
|
||||
for (int i = 0; i < sample_features.size(); ++i)
|
||||
fcinfo.cloud_features.SetBit(sample_features[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds all fonts of the given class to the shape.
|
||||
void TrainingSampleSet::AddAllFontsForClass(int class_id, Shape* shape) const {
|
||||
for (int f = 0; f < font_id_map_.CompactSize(); ++f) {
|
||||
int font_id = font_id_map_.CompactToSparse(f);
|
||||
shape->AddToShape(class_id, font_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Display the samples with the given indexed feature that also match
|
||||
// the given shape.
|
||||
void TrainingSampleSet::DisplaySamplesWithFeature(int f_index,
|
||||
const Shape& shape,
|
||||
const IntFeatureSpace& space,
|
||||
ScrollView::Color color,
|
||||
ScrollView* window) const {
|
||||
for (int s = 0; s < num_raw_samples(); ++s) {
|
||||
const TrainingSample* sample = GetSample(s);
|
||||
if (shape.ContainsUnichar(sample->class_id())) {
|
||||
GenericVector<int> indexed_features;
|
||||
space.IndexAndSortFeatures(sample->features(), sample->num_features(),
|
||||
&indexed_features);
|
||||
for (int f = 0; f < indexed_features.size(); ++f) {
|
||||
if (indexed_features[f] == f_index) {
|
||||
sample->DisplayFeatures(color, window);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace tesseract.
|
|
@ -1,198 +0,0 @@
|
|||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
// Author: rays@google.com (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 THIRD_PARTY_TESSERACT_CLASSIFY_ERRORCOUNTER_H_
|
||||
#define THIRD_PARTY_TESSERACT_CLASSIFY_ERRORCOUNTER_H_
|
||||
|
||||
#include "genericvector.h"
|
||||
#include "matrix.h"
|
||||
|
||||
struct Pix;
|
||||
template <typename T> class UnicityTable;
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
struct FontInfo;
|
||||
class SampleIterator;
|
||||
class ShapeClassifier;
|
||||
class ShapeRating;
|
||||
class ShapeTable;
|
||||
class TrainingSample;
|
||||
|
||||
// Enumeration of the different types of error count.
|
||||
// Error counts work as follows:
|
||||
//
|
||||
// Ground truth is a valid unichar-id / font-id pair:
|
||||
// Number of classifier answers?
|
||||
// 0 >0
|
||||
// CT_REJECT BOTH unichar-id and font-id match top shape?
|
||||
// __________ yes! no
|
||||
// CT_SHAPE_TOP_CORRECT CT_SHAPE_TOP_ERR
|
||||
// | Font attributes match?
|
||||
// | yes! no
|
||||
// | | CT_FONT_ATTR_ERROR
|
||||
// | Top unichar-id matches?
|
||||
// | yes! no
|
||||
// Top shape-id has multiple unichars? CT_UNICHAR_TOP1_ERR
|
||||
// yes! no 2nd shape unichar id matches?
|
||||
// CT_OK_MULTI_UNICHAR ________ yes! no
|
||||
// ___________________ _____ CT_UNICHAR_TOP2_ERR
|
||||
// Any unichar-id matches?
|
||||
// yes! no
|
||||
// ______ CT_UNICHAR_TOPN_ERR
|
||||
// _________________
|
||||
// Note that multiple counts may be activated for a single sample!
|
||||
//
|
||||
// Ground truth is for a fragment/n-gram that is NOT in the unicharset.
|
||||
// This is called junk and is expected to be rejected:
|
||||
// Number of classifier answers?
|
||||
// 0 >0
|
||||
// CT_REJECTED_JUNK CT_ACCEPTED_JUNK
|
||||
//
|
||||
// Also, CT_NUM_RESULTS stores the mean number of results, and CT_RANK stores
|
||||
// the mean rank of the correct result, counting from 0, and with an error
|
||||
// receiving the number of answers as the correct rank.
|
||||
//
|
||||
// Keep in sync with the ReportString function.
|
||||
enum CountTypes {
|
||||
CT_SHAPE_TOP_CORRECT, // Top shape id is actually correct.
|
||||
CT_SHAPE_TOP_ERR, // Top shape id is not correct.
|
||||
CT_FONT_ATTR_ERR, // Font attributes incorrect, ignoring unichar.
|
||||
CT_UNICHAR_TOP1_ERR, // Top shape does not contain correct unichar id.
|
||||
CT_UNICHAR_TOP2_ERR, // Top 2 shapes don't contain correct unichar id.
|
||||
CT_UNICHAR_TOPN_ERR, // No output shape contains correct unichar id.
|
||||
CT_OK_MULTI_UNICHAR, // Top shape id has correct unichar id, and others.
|
||||
CT_REJECT, // Classifier hates this.
|
||||
CT_NUM_RESULTS, // Number of answers produced.
|
||||
CT_RANK, // Rank of correct answer.
|
||||
CT_REJECTED_JUNK, // Junk that was correctly rejected.
|
||||
CT_ACCEPTED_JUNK, // Junk that was incorrectly classified otherwise.
|
||||
|
||||
CT_SIZE // Number of types for array sizing.
|
||||
};
|
||||
|
||||
// Class to encapsulate all the functionality and sub-structures required
|
||||
// to count errors for an isolated character classifier (ShapeClassifier).
|
||||
class ErrorCounter {
|
||||
public:
|
||||
// Computes and returns the unweighted boosting_mode error rate of the given
|
||||
// classifier. Can be used for testing, or inside an iterative training
|
||||
// system, including one that uses boosting.
|
||||
// report_levels:
|
||||
// 0 = no output.
|
||||
// 1 = bottom-line error rate.
|
||||
// 2 = bottom-line error rate + time.
|
||||
// 3 = font-level error rate + time.
|
||||
// 4 = list of all errors + short classifier debug output on 16 errors.
|
||||
// 5 = list of all errors + short classifier debug output on 25 errors.
|
||||
// * The boosting_mode determines which error type is used for computing the
|
||||
// scaled_error output, and setting the is_error flag in the samples.
|
||||
// * The fontinfo_table is used to get string font names for the debug
|
||||
// output, and also to count font attributes errors.
|
||||
// * The page_images vector may contain a Pix* (which may be NULL) for each
|
||||
// page index assigned to the samples.
|
||||
// * The it provides encapsulated iteration over some sample set.
|
||||
// * The outputs unichar_error, scaled_error and totals_report are all
|
||||
// optional.
|
||||
// * If not NULL, unichar error gets the top1 unichar error rate.
|
||||
// * Scaled_error gets the error chosen by boosting_mode weighted by the
|
||||
// weights on the samples.
|
||||
// * Fonts_report gets a string summarizing the error rates for each font in
|
||||
// both human-readable form and as a tab-separated list of error counts.
|
||||
// The human-readable form is all before the first tab.
|
||||
// * The return value is the un-weighted version of the scaled_error.
|
||||
static double ComputeErrorRate(ShapeClassifier* classifier,
|
||||
int report_level, CountTypes boosting_mode,
|
||||
const UnicityTable<FontInfo>& fontinfo_table,
|
||||
const GenericVector<Pix*>& page_images,
|
||||
SampleIterator* it,
|
||||
double* unichar_error,
|
||||
double* scaled_error,
|
||||
STRING* fonts_report);
|
||||
|
||||
private:
|
||||
// Simple struct to hold an array of counts.
|
||||
struct Counts {
|
||||
Counts();
|
||||
// Adds other into this for computing totals.
|
||||
void operator+=(const Counts& other);
|
||||
|
||||
int n[CT_SIZE];
|
||||
};
|
||||
|
||||
// Constructor is private. Only anticipated use of ErrorCounter is via
|
||||
// the static ComputeErrorRate.
|
||||
ErrorCounter(int charsetsize, int shapesize, int fontsize);
|
||||
~ErrorCounter();
|
||||
|
||||
// Accumulates the errors from the classifier results on a single sample.
|
||||
// Returns true if debug is true and a CT_UNICHAR_TOPN_ERR error occurred.
|
||||
// boosting_mode selects the type of error to be used for boosting and the
|
||||
// is_error_ member of sample is set according to whether the required type
|
||||
// of error occurred. The font_table provides access to font properties
|
||||
// for error counting and shape_table is used to understand the relationship
|
||||
// between unichar_ids and shape_ids in the results
|
||||
bool AccumulateErrors(bool debug, CountTypes boosting_mode,
|
||||
const UnicityTable<FontInfo>& font_table,
|
||||
const ShapeTable& shape_table,
|
||||
const GenericVector<ShapeRating>& results,
|
||||
TrainingSample* sample);
|
||||
|
||||
// Accumulates counts for junk. Counts only whether the junk was correctly
|
||||
// rejected or not.
|
||||
void AccumulateJunk(const ShapeTable& shape_table,
|
||||
const GenericVector<ShapeRating>& results,
|
||||
TrainingSample* sample);
|
||||
|
||||
// Creates a report of the error rate. The report_level controls the detail
|
||||
// that is reported to stderr via tprintf:
|
||||
// 0 -> no output.
|
||||
// >=1 -> bottom-line error rate.
|
||||
// >=3 -> font-level error rate.
|
||||
// boosting_mode determines the return value. It selects which (un-weighted)
|
||||
// error rate to return.
|
||||
// The fontinfo_table from MasterTrainer provides the names of fonts.
|
||||
// The it determines the current subset of the training samples.
|
||||
// If not NULL, the top-choice unichar error rate is saved in unichar_error.
|
||||
// If not NULL, the report string is saved in fonts_report.
|
||||
// (Ignoring report_level).
|
||||
double ReportErrors(int report_level, CountTypes boosting_mode,
|
||||
const UnicityTable<FontInfo>& fontinfo_table,
|
||||
const SampleIterator& it,
|
||||
double* unichar_error,
|
||||
STRING* fonts_report);
|
||||
|
||||
// Sets the report string to a combined human and machine-readable report
|
||||
// string of the error rates.
|
||||
// Returns false if there is no data, leaving report unchanged.
|
||||
static bool ReportString(const Counts& counts, STRING* report);
|
||||
|
||||
// Computes the error rates and returns in rates which is an array of size
|
||||
// CT_SIZE. Returns false if there is no data, leaving rates unchanged.
|
||||
static bool ComputeRates(const Counts& counts, double rates[CT_SIZE]);
|
||||
|
||||
|
||||
// Total scaled error used by boosting algorithms.
|
||||
double scaled_error_;
|
||||
// Vector indexed by font_id from the samples of error accumulators.
|
||||
GenericVector<Counts> font_counts_;
|
||||
// Counts of the results that map each unichar_id (from samples) to an
|
||||
// incorrect shape_id.
|
||||
GENERIC_2D_ARRAY<int> unichar_counts_;
|
||||
};
|
||||
|
||||
} // namespace tesseract.
|
||||
|
||||
#endif /* THIRD_PARTY_TESSERACT_CLASSIFY_ERRORCOUNTER_H_ */
|
|
@ -1,143 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////
|
||||
// File: workingpartset.cpp
|
||||
// Description: Class to hold a working set of partitions of the page
|
||||
// during construction of text/image regions.
|
||||
// Author: Ray Smith
|
||||
// Created: Tue Ocr 28 17:21:01 PDT 2008
|
||||
//
|
||||
// (C) Copyright 2008, 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 "workingpartset.h"
|
||||
#include "colpartition.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
ELISTIZE(WorkingPartSet)
|
||||
|
||||
// Add the partition to this WorkingPartSet. Unrelated partitions are
|
||||
// stored in the order in which they are received, but if the partition
|
||||
// has a SingletonPartner, make sure that it stays with its partner.
|
||||
void WorkingPartSet::AddPartition(ColPartition* part) {
|
||||
ColPartition* partner = part->SingletonPartner(true);
|
||||
if (partner != NULL) {
|
||||
ASSERT_HOST(partner->SingletonPartner(false) == part);
|
||||
}
|
||||
if (latest_part_ == NULL || partner == NULL) {
|
||||
// This partition goes at the end of the list
|
||||
part_it_.move_to_last();
|
||||
} else if (latest_part_->SingletonPartner(false) != part) {
|
||||
// Reposition the iterator to the correct partner, or at the end.
|
||||
for (part_it_.move_to_first(); !part_it_.at_last() &&
|
||||
part_it_.data() != partner;
|
||||
part_it_.forward());
|
||||
}
|
||||
part_it_.add_after_then_move(part);
|
||||
latest_part_ = part;
|
||||
}
|
||||
|
||||
// Make blocks out of any partitions in this WorkingPartSet, and append
|
||||
// them to the end of the blocks list. bleft, tright and resolution give
|
||||
// the bounds and resolution of the source image, so that blocks can be
|
||||
// made to fit in the bounds.
|
||||
// All ColPartitions go in the used_parts list, as they need to be kept
|
||||
// around, but are no longer needed.
|
||||
void WorkingPartSet::ExtractCompletedBlocks(const ICOORD& bleft,
|
||||
const ICOORD& tright,
|
||||
int resolution,
|
||||
ColPartition_LIST* used_parts,
|
||||
BLOCK_LIST* blocks,
|
||||
TO_BLOCK_LIST* to_blocks) {
|
||||
MakeBlocks(bleft, tright, resolution, used_parts);
|
||||
BLOCK_IT block_it(blocks);
|
||||
block_it.move_to_last();
|
||||
block_it.add_list_after(&completed_blocks_);
|
||||
TO_BLOCK_IT to_block_it(to_blocks);
|
||||
to_block_it.move_to_last();
|
||||
to_block_it.add_list_after(&to_blocks_);
|
||||
}
|
||||
|
||||
// Insert the given blocks at the front of the completed_blocks_ list so
|
||||
// they can be kept in the correct reading order.
|
||||
void WorkingPartSet::InsertCompletedBlocks(BLOCK_LIST* blocks,
|
||||
TO_BLOCK_LIST* to_blocks) {
|
||||
BLOCK_IT block_it(&completed_blocks_);
|
||||
block_it.add_list_before(blocks);
|
||||
TO_BLOCK_IT to_block_it(&to_blocks_);
|
||||
to_block_it.add_list_before(to_blocks);
|
||||
}
|
||||
|
||||
// Make a block using lines parallel to the given vector that fit between
|
||||
// the min and max coordinates specified by the ColPartitions.
|
||||
// Construct a block from the given list of partitions.
|
||||
void WorkingPartSet::MakeBlocks(const ICOORD& bleft, const ICOORD& tright,
|
||||
int resolution, ColPartition_LIST* used_parts) {
|
||||
part_it_.move_to_first();
|
||||
while (!part_it_.empty()) {
|
||||
// Gather a list of ColPartitions in block_parts that will be split
|
||||
// by linespacing into smaller blocks.
|
||||
ColPartition_LIST block_parts;
|
||||
ColPartition_IT block_it(&block_parts);
|
||||
ColPartition* next_part = NULL;
|
||||
bool text_block = false;
|
||||
do {
|
||||
ColPartition* part = part_it_.extract();
|
||||
if (part->blob_type() == BRT_UNKNOWN || part->blob_type() == BRT_TEXT)
|
||||
text_block = true;
|
||||
part->set_working_set(NULL);
|
||||
part_it_.forward();
|
||||
block_it.add_after_then_move(part);
|
||||
next_part = part->SingletonPartner(false);
|
||||
if (part_it_.empty() || next_part != part_it_.data()) {
|
||||
// Sequences of partitions can get split by titles.
|
||||
next_part = NULL;
|
||||
}
|
||||
// Merge adjacent blocks that are of the same type and let the
|
||||
// linespacing determine the real boundaries.
|
||||
if (next_part == NULL && !part_it_.empty()) {
|
||||
ColPartition* next_block_part = part_it_.data();
|
||||
const TBOX& part_box = part->bounding_box();
|
||||
const TBOX& next_box = next_block_part->bounding_box();
|
||||
|
||||
// In addition to the same type, the next box must not be above the
|
||||
// current box, nor (if image) too far below.
|
||||
PolyBlockType type = part->type(), next_type = next_block_part->type();
|
||||
if (ColPartition::TypesSimilar(type, next_type) &&
|
||||
next_box.bottom() <= part_box.top() &&
|
||||
(text_block ||
|
||||
part_box.bottom() - next_box.top() < part_box.height()))
|
||||
next_part = next_block_part;
|
||||
}
|
||||
} while (!part_it_.empty() && next_part != NULL);
|
||||
if (!text_block) {
|
||||
TO_BLOCK* to_block = ColPartition::MakeBlock(bleft, tright,
|
||||
&block_parts, used_parts);
|
||||
if (to_block != NULL) {
|
||||
TO_BLOCK_IT to_block_it(&to_blocks_);
|
||||
to_block_it.add_to_end(to_block);
|
||||
BLOCK_IT block_it(&completed_blocks_);
|
||||
block_it.add_to_end(to_block->block);
|
||||
}
|
||||
} else {
|
||||
// Further sub-divide text blocks where linespacing changes.
|
||||
ColPartition::LineSpacingBlocks(bleft, tright, resolution, &block_parts,
|
||||
used_parts,
|
||||
&completed_blocks_, &to_blocks_);
|
||||
}
|
||||
}
|
||||
part_it_.set_to_list(&part_set_);
|
||||
latest_part_ = NULL;
|
||||
ASSERT_HOST(completed_blocks_.length() == to_blocks_.length());
|
||||
}
|
||||
|
||||
} // namespace tesseract.
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright 2007 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); You may not
|
||||
// use this file except in compliance with the License. You may obtain a copy of
|
||||
// the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
|
||||
// applicable law or agreed to in writing, software distributed under the
|
||||
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
|
||||
// OF ANY KIND, either express or implied. See the License for the specific
|
||||
// language governing permissions and limitations under the License.
|
||||
|
||||
package com.google.scrollview.ui;
|
||||
|
||||
/**
|
||||
* A MenuListItem is any sort of menu entry. This can either be within a popup
|
||||
* menu or within a menubar. It can either be a submenu (only name and
|
||||
* command-id) or a name with an associated value and possibly description. They
|
||||
* can also have new entries added (if they are submenus).
|
||||
*
|
||||
* @author wanke@google.com
|
||||
*/
|
||||
|
||||
import com.google.scrollview.ScrollView;
|
||||
import com.google.scrollview.events.SVEvent;
|
||||
import com.google.scrollview.events.SVEventType;
|
||||
|
||||
import javax.swing.JMenuItem;
|
||||
|
||||
/**
|
||||
* Constructs a new menulistitem which just has an ID and a name attached to
|
||||
* it. In this case, we will have to ask for the value of the item and its
|
||||
* description if it gets called.
|
||||
*/
|
||||
class SVEmptyMenuItem extends SVAbstractMenuItem {
|
||||
SVEmptyMenuItem(int id, String name) {
|
||||
super(id, name, new JMenuItem(name));
|
||||
}
|
||||
/** What to do when user clicks on this item. */
|
||||
@Override
|
||||
public void performAction(SVWindow window, SVEventType eventType) {
|
||||
// Send an event indicating that someone clicked on an entry.
|
||||
// Value will be null here.
|
||||
SVEvent svme =
|
||||
new SVEvent(eventType, window, id, getValue());
|
||||
ScrollView.addMessage(svme);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "windows.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,2,0,0
|
||||
PRODUCTVERSION 3,2,0,0
|
||||
FILEFLAGSMASK 0x17L
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "FileDescription", "Character normalization training for Tesseract"
|
||||
VALUE "FileVersion", "3,2,0,0"
|
||||
VALUE "InternalName", "cntraining"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2012 Google, Inc. Licensed under the Apache License, Version 2.0"
|
||||
VALUE "OriginalFilename", "cntraining.exe"
|
||||
VALUE "ProductName", "Tesseract OCR"
|
||||
VALUE "ProductVersion", "3.02"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
|
@ -1,523 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: tess_lang_model.cpp
|
||||
* Description: Implementation of the Tesseract Language Model Class
|
||||
* Author: Ahmad Abdulkader
|
||||
* Created: 2008
|
||||
*
|
||||
* (C) Copyright 2008, 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.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
// The TessLangModel class abstracts the Tesseract language model. It inherits
|
||||
// from the LangModel class. The Tesseract language model encompasses several
|
||||
// Dawgs (words from training data, punctuation, numbers, document words).
|
||||
// On top of this Cube adds an OOD state machine
|
||||
// The class provides methods to traverse the language model in a generative
|
||||
// fashion. Given any node in the DAWG, the language model can generate a list
|
||||
// of children (or fan-out) edges
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "char_samp.h"
|
||||
#include "cube_utils.h"
|
||||
#include "dict.h"
|
||||
#include "tesseractclass.h"
|
||||
#include "tess_lang_model.h"
|
||||
#include "tessdatamanager.h"
|
||||
#include "unicharset.h"
|
||||
|
||||
namespace tesseract {
|
||||
// max fan-out (used for preallocation). Initialized here, but modified by
|
||||
// constructor
|
||||
int TessLangModel::max_edge_ = 4096;
|
||||
|
||||
// Language model extra State machines
|
||||
const Dawg *TessLangModel::ood_dawg_ = reinterpret_cast<Dawg *>(DAWG_OOD);
|
||||
const Dawg *TessLangModel::number_dawg_ = reinterpret_cast<Dawg *>(DAWG_NUMBER);
|
||||
|
||||
// number state machine
|
||||
const int TessLangModel::num_state_machine_[kStateCnt][kNumLiteralCnt] = {
|
||||
{0, 1, 1, NUM_TRM, NUM_TRM},
|
||||
{NUM_TRM, 1, 1, 3, 2},
|
||||
{NUM_TRM, NUM_TRM, 1, NUM_TRM, 2},
|
||||
{NUM_TRM, NUM_TRM, 3, NUM_TRM, 2},
|
||||
};
|
||||
const int TessLangModel::num_max_repeat_[kStateCnt] = {3, 32, 8, 3};
|
||||
|
||||
// thresholds and penalties
|
||||
int TessLangModel::max_ood_shape_cost_ = CubeUtils::Prob2Cost(1e-4);
|
||||
|
||||
TessLangModel::TessLangModel(const string &lm_params,
|
||||
const string &data_file_path,
|
||||
bool load_system_dawg,
|
||||
TessdataManager *tessdata_manager,
|
||||
CubeRecoContext *cntxt) {
|
||||
cntxt_ = cntxt;
|
||||
has_case_ = cntxt_->HasCase();
|
||||
// Load the rest of the language model elements from file
|
||||
LoadLangModelElements(lm_params);
|
||||
// Load word_dawgs_ if needed.
|
||||
if (tessdata_manager->SeekToStart(TESSDATA_CUBE_UNICHARSET)) {
|
||||
word_dawgs_ = new DawgVector();
|
||||
if (load_system_dawg &&
|
||||
tessdata_manager->SeekToStart(TESSDATA_CUBE_SYSTEM_DAWG)) {
|
||||
// The last parameter to the Dawg constructor (the debug level) is set to
|
||||
// false, until Cube has a way to express its preferred debug level.
|
||||
*word_dawgs_ += new SquishedDawg(tessdata_manager->GetDataFilePtr(),
|
||||
DAWG_TYPE_WORD,
|
||||
cntxt_->Lang().c_str(),
|
||||
SYSTEM_DAWG_PERM, false);
|
||||
}
|
||||
} else {
|
||||
word_dawgs_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup an edge array
|
||||
void TessLangModel::FreeEdges(int edge_cnt, LangModEdge **edge_array) {
|
||||
if (edge_array != NULL) {
|
||||
for (int edge_idx = 0; edge_idx < edge_cnt; edge_idx++) {
|
||||
if (edge_array[edge_idx] != NULL) {
|
||||
delete edge_array[edge_idx];
|
||||
}
|
||||
}
|
||||
delete []edge_array;
|
||||
}
|
||||
}
|
||||
|
||||
// Determines if a sequence of 32-bit chars is valid in this language model
|
||||
// starting from the specified edge. If the eow_flag is ON, also checks for
|
||||
// a valid EndOfWord. If final_edge is not NULL, returns a pointer to the last
|
||||
// edge
|
||||
bool TessLangModel::IsValidSequence(LangModEdge *edge,
|
||||
const char_32 *sequence,
|
||||
bool eow_flag,
|
||||
LangModEdge **final_edge) {
|
||||
// get the edges emerging from this edge
|
||||
int edge_cnt = 0;
|
||||
LangModEdge **edge_array = GetEdges(NULL, edge, &edge_cnt);
|
||||
|
||||
// find the 1st char in the sequence in the children
|
||||
for (int edge_idx = 0; edge_idx < edge_cnt; edge_idx++) {
|
||||
// found a match
|
||||
if (sequence[0] == edge_array[edge_idx]->EdgeString()[0]) {
|
||||
// if this is the last char
|
||||
if (sequence[1] == 0) {
|
||||
// succeed if we are in prefix mode or this is a terminal edge
|
||||
if (eow_flag == false || edge_array[edge_idx]->IsEOW()) {
|
||||
if (final_edge != NULL) {
|
||||
(*final_edge) = edge_array[edge_idx];
|
||||
edge_array[edge_idx] = NULL;
|
||||
}
|
||||
|
||||
FreeEdges(edge_cnt, edge_array);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// not the last char continue checking
|
||||
if (IsValidSequence(edge_array[edge_idx], sequence + 1, eow_flag,
|
||||
final_edge) == true) {
|
||||
FreeEdges(edge_cnt, edge_array);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeEdges(edge_cnt, edge_array);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determines if a sequence of 32-bit chars is valid in this language model
|
||||
// starting from the root. If the eow_flag is ON, also checks for
|
||||
// a valid EndOfWord. If final_edge is not NULL, returns a pointer to the last
|
||||
// edge
|
||||
bool TessLangModel::IsValidSequence(const char_32 *sequence, bool eow_flag,
|
||||
LangModEdge **final_edge) {
|
||||
if (final_edge != NULL) {
|
||||
(*final_edge) = NULL;
|
||||
}
|
||||
|
||||
return IsValidSequence(NULL, sequence, eow_flag, final_edge);
|
||||
}
|
||||
|
||||
bool TessLangModel::IsLeadingPunc(const char_32 ch) {
|
||||
return lead_punc_.find(ch) != string::npos;
|
||||
}
|
||||
|
||||
bool TessLangModel::IsTrailingPunc(const char_32 ch) {
|
||||
return trail_punc_.find(ch) != string::npos;
|
||||
}
|
||||
|
||||
bool TessLangModel::IsDigit(const char_32 ch) {
|
||||
return digits_.find(ch) != string::npos;
|
||||
}
|
||||
|
||||
// The general fan-out generation function. Returns the list of edges
|
||||
// fanning-out of the specified edge and their count. If an AltList is
|
||||
// specified, only the class-ids with a minimum cost are considered
|
||||
LangModEdge ** TessLangModel::GetEdges(CharAltList *alt_list,
|
||||
LangModEdge *lang_mod_edge,
|
||||
int *edge_cnt) {
|
||||
TessLangModEdge *tess_lm_edge =
|
||||
reinterpret_cast<TessLangModEdge *>(lang_mod_edge);
|
||||
LangModEdge **edge_array = NULL;
|
||||
(*edge_cnt) = 0;
|
||||
|
||||
// if we are starting from the root, we'll instantiate every DAWG
|
||||
// and get the all the edges that emerge from the root
|
||||
if (tess_lm_edge == NULL) {
|
||||
// get DAWG count from Tesseract
|
||||
int dawg_cnt = NumDawgs();
|
||||
// preallocate the edge buffer
|
||||
(*edge_cnt) = dawg_cnt * max_edge_;
|
||||
edge_array = new LangModEdge *[(*edge_cnt)];
|
||||
if (edge_array == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int dawg_idx = (*edge_cnt) = 0; dawg_idx < dawg_cnt; dawg_idx++) {
|
||||
const Dawg *curr_dawg = GetDawg(dawg_idx);
|
||||
// Only look through word Dawgs (since there is a special way of
|
||||
// handling numbers and punctuation).
|
||||
if (curr_dawg->type() == DAWG_TYPE_WORD) {
|
||||
(*edge_cnt) += FanOut(alt_list, curr_dawg, 0, 0, NULL, true,
|
||||
edge_array + (*edge_cnt));
|
||||
}
|
||||
} // dawg
|
||||
|
||||
(*edge_cnt) += FanOut(alt_list, number_dawg_, 0, 0, NULL, true,
|
||||
edge_array + (*edge_cnt));
|
||||
|
||||
// OOD: it is intentionally not added to the list to make sure it comes
|
||||
// at the end
|
||||
(*edge_cnt) += FanOut(alt_list, ood_dawg_, 0, 0, NULL, true,
|
||||
edge_array + (*edge_cnt));
|
||||
|
||||
// set the root flag for all root edges
|
||||
for (int edge_idx = 0; edge_idx < (*edge_cnt); edge_idx++) {
|
||||
edge_array[edge_idx]->SetRoot(true);
|
||||
}
|
||||
} else { // not starting at the root
|
||||
// preallocate the edge buffer
|
||||
(*edge_cnt) = max_edge_;
|
||||
// allocate memory for edges
|
||||
edge_array = new LangModEdge *[(*edge_cnt)];
|
||||
if (edge_array == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get the FanOut edges from the root of each dawg
|
||||
(*edge_cnt) = FanOut(alt_list,
|
||||
tess_lm_edge->GetDawg(),
|
||||
tess_lm_edge->EndEdge(), tess_lm_edge->EdgeMask(),
|
||||
tess_lm_edge->EdgeString(), false, edge_array);
|
||||
}
|
||||
return edge_array;
|
||||
}
|
||||
|
||||
// generate edges from an NULL terminated string
|
||||
// (used for punctuation, operators and digits)
|
||||
int TessLangModel::Edges(const char *strng, const Dawg *dawg,
|
||||
EDGE_REF edge_ref, EDGE_REF edge_mask,
|
||||
LangModEdge **edge_array) {
|
||||
int edge_idx,
|
||||
edge_cnt = 0;
|
||||
|
||||
for (edge_idx = 0; strng[edge_idx] != 0; edge_idx++) {
|
||||
int class_id = cntxt_->CharacterSet()->ClassID((char_32)strng[edge_idx]);
|
||||
if (class_id != INVALID_UNICHAR_ID) {
|
||||
// create an edge object
|
||||
edge_array[edge_cnt] = new TessLangModEdge(cntxt_, dawg, edge_ref,
|
||||
class_id);
|
||||
if (edge_array[edge_cnt] == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
reinterpret_cast<TessLangModEdge *>(edge_array[edge_cnt])->
|
||||
SetEdgeMask(edge_mask);
|
||||
edge_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
return edge_cnt;
|
||||
}
|
||||
|
||||
// generate OOD edges
|
||||
int TessLangModel::OODEdges(CharAltList *alt_list, EDGE_REF edge_ref,
|
||||
EDGE_REF edge_ref_mask, LangModEdge **edge_array) {
|
||||
int class_cnt = cntxt_->CharacterSet()->ClassCount();
|
||||
int edge_cnt = 0;
|
||||
for (int class_id = 0; class_id < class_cnt; class_id++) {
|
||||
// produce an OOD edge only if the cost of the char is low enough
|
||||
if ((alt_list == NULL ||
|
||||
alt_list->ClassCost(class_id) <= max_ood_shape_cost_)) {
|
||||
// create an edge object
|
||||
edge_array[edge_cnt] = new TessLangModEdge(cntxt_, class_id);
|
||||
if (edge_array[edge_cnt] == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
edge_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
return edge_cnt;
|
||||
}
|
||||
|
||||
// computes and returns the edges that fan out of an edge ref
|
||||
int TessLangModel::FanOut(CharAltList *alt_list, const Dawg *dawg,
|
||||
EDGE_REF edge_ref, EDGE_REF edge_mask,
|
||||
const char_32 *str, bool root_flag,
|
||||
LangModEdge **edge_array) {
|
||||
int edge_cnt = 0;
|
||||
NODE_REF next_node = NO_EDGE;
|
||||
|
||||
// OOD
|
||||
if (dawg == reinterpret_cast<Dawg *>(DAWG_OOD)) {
|
||||
if (ood_enabled_ == true) {
|
||||
return OODEdges(alt_list, edge_ref, edge_mask, edge_array);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else if (dawg == reinterpret_cast<Dawg *>(DAWG_NUMBER)) {
|
||||
// Number
|
||||
if (numeric_enabled_ == true) {
|
||||
return NumberEdges(edge_ref, edge_array);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else if (IsTrailingPuncEdge(edge_mask)) {
|
||||
// a TRAILING PUNC MASK, generate more trailing punctuation and return
|
||||
if (punc_enabled_ == true) {
|
||||
EDGE_REF trail_cnt = TrailingPuncCount(edge_mask);
|
||||
return Edges(trail_punc_.c_str(), dawg, edge_ref,
|
||||
TrailingPuncEdgeMask(trail_cnt + 1), edge_array);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else if (root_flag == true || edge_ref == 0) {
|
||||
// Root, generate leading punctuation and continue
|
||||
if (root_flag) {
|
||||
if (punc_enabled_ == true) {
|
||||
edge_cnt += Edges(lead_punc_.c_str(), dawg, 0, LEAD_PUNC_EDGE_REF_MASK,
|
||||
edge_array);
|
||||
}
|
||||
}
|
||||
next_node = 0;
|
||||
} else {
|
||||
// a node in the main trie
|
||||
bool eow_flag = (dawg->end_of_word(edge_ref) != 0);
|
||||
|
||||
// for EOW
|
||||
if (eow_flag == true) {
|
||||
// generate trailing punctuation
|
||||
if (punc_enabled_ == true) {
|
||||
edge_cnt += Edges(trail_punc_.c_str(), dawg, edge_ref,
|
||||
TrailingPuncEdgeMask((EDGE_REF)1), edge_array);
|
||||
// generate a hyphen and go back to the root
|
||||
edge_cnt += Edges("-/", dawg, 0, 0, edge_array + edge_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
// advance node
|
||||
next_node = dawg->next_node(edge_ref);
|
||||
if (next_node == 0 || next_node == NO_EDGE) {
|
||||
return edge_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
// now get all the emerging edges if word list is enabled
|
||||
if (word_list_enabled_ == true && next_node != NO_EDGE) {
|
||||
// create child edges
|
||||
int child_edge_cnt =
|
||||
TessLangModEdge::CreateChildren(cntxt_, dawg, next_node,
|
||||
edge_array + edge_cnt);
|
||||
int strt_cnt = edge_cnt;
|
||||
|
||||
// set the edge mask
|
||||
for (int child = 0; child < child_edge_cnt; child++) {
|
||||
reinterpret_cast<TessLangModEdge *>(edge_array[edge_cnt++])->
|
||||
SetEdgeMask(edge_mask);
|
||||
}
|
||||
|
||||
// if we are at the root, create upper case forms of these edges if possible
|
||||
if (root_flag == true) {
|
||||
for (int child = 0; child < child_edge_cnt; child++) {
|
||||
TessLangModEdge *child_edge =
|
||||
reinterpret_cast<TessLangModEdge *>(edge_array[strt_cnt + child]);
|
||||
|
||||
if (has_case_ == true) {
|
||||
const char_32 *edge_str = child_edge->EdgeString();
|
||||
if (edge_str != NULL && islower(edge_str[0]) != 0 &&
|
||||
edge_str[1] == 0) {
|
||||
int class_id =
|
||||
cntxt_->CharacterSet()->ClassID(toupper(edge_str[0]));
|
||||
if (class_id != INVALID_UNICHAR_ID) {
|
||||
// generate an upper case edge for lower case chars
|
||||
edge_array[edge_cnt] = new TessLangModEdge(cntxt_, dawg,
|
||||
child_edge->StartEdge(), child_edge->EndEdge(), class_id);
|
||||
|
||||
if (edge_array[edge_cnt] != NULL) {
|
||||
reinterpret_cast<TessLangModEdge *>(edge_array[edge_cnt])->
|
||||
SetEdgeMask(edge_mask);
|
||||
edge_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return edge_cnt;
|
||||
}
|
||||
|
||||
// Generate the edges fanning-out from an edge in the number state machine
|
||||
int TessLangModel::NumberEdges(EDGE_REF edge_ref, LangModEdge **edge_array) {
|
||||
EDGE_REF new_state,
|
||||
state;
|
||||
|
||||
int repeat_cnt,
|
||||
new_repeat_cnt;
|
||||
|
||||
state = ((edge_ref & NUMBER_STATE_MASK) >> NUMBER_STATE_SHIFT);
|
||||
repeat_cnt = ((edge_ref & NUMBER_REPEAT_MASK) >> NUMBER_REPEAT_SHIFT);
|
||||
|
||||
if (state < 0 || state >= kStateCnt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// go thru all valid transitions from the state
|
||||
int edge_cnt = 0;
|
||||
|
||||
EDGE_REF new_edge_ref;
|
||||
|
||||
for (int lit = 0; lit < kNumLiteralCnt; lit++) {
|
||||
// move to the new state
|
||||
new_state = num_state_machine_[state][lit];
|
||||
if (new_state == NUM_TRM) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (new_state == state) {
|
||||
new_repeat_cnt = repeat_cnt + 1;
|
||||
} else {
|
||||
new_repeat_cnt = 1;
|
||||
}
|
||||
|
||||
// not allowed to repeat beyond this
|
||||
if (new_repeat_cnt > num_max_repeat_[state]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
new_edge_ref = (new_state << NUMBER_STATE_SHIFT) |
|
||||
(lit << NUMBER_LITERAL_SHIFT) |
|
||||
(new_repeat_cnt << NUMBER_REPEAT_SHIFT);
|
||||
|
||||
edge_cnt += Edges(literal_str_[lit]->c_str(), number_dawg_,
|
||||
new_edge_ref, 0, edge_array + edge_cnt);
|
||||
}
|
||||
|
||||
return edge_cnt;
|
||||
}
|
||||
|
||||
// Loads Language model elements from contents of the <lang>.cube.lm file
|
||||
bool TessLangModel::LoadLangModelElements(const string &lm_params) {
|
||||
bool success = true;
|
||||
// split into lines, each corresponding to a token type below
|
||||
vector<string> str_vec;
|
||||
CubeUtils::SplitStringUsing(lm_params, "\r\n", &str_vec);
|
||||
for (int entry = 0; entry < str_vec.size(); entry++) {
|
||||
vector<string> tokens;
|
||||
// should be only two tokens: type and value
|
||||
CubeUtils::SplitStringUsing(str_vec[entry], "=", &tokens);
|
||||
if (tokens.size() != 2)
|
||||
success = false;
|
||||
if (tokens[0] == "LeadPunc") {
|
||||
lead_punc_ = tokens[1];
|
||||
} else if (tokens[0] == "TrailPunc") {
|
||||
trail_punc_ = tokens[1];
|
||||
} else if (tokens[0] == "NumLeadPunc") {
|
||||
num_lead_punc_ = tokens[1];
|
||||
} else if (tokens[0] == "NumTrailPunc") {
|
||||
num_trail_punc_ = tokens[1];
|
||||
} else if (tokens[0] == "Operators") {
|
||||
operators_ = tokens[1];
|
||||
} else if (tokens[0] == "Digits") {
|
||||
digits_ = tokens[1];
|
||||
} else if (tokens[0] == "Alphas") {
|
||||
alphas_ = tokens[1];
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
RemoveInvalidCharacters(&num_lead_punc_);
|
||||
RemoveInvalidCharacters(&num_trail_punc_);
|
||||
RemoveInvalidCharacters(&digits_);
|
||||
RemoveInvalidCharacters(&operators_);
|
||||
RemoveInvalidCharacters(&alphas_);
|
||||
|
||||
// form the array of literal strings needed for number state machine
|
||||
// It is essential that the literal strings go in the order below
|
||||
literal_str_[0] = &num_lead_punc_;
|
||||
literal_str_[1] = &num_trail_punc_;
|
||||
literal_str_[2] = &digits_;
|
||||
literal_str_[3] = &operators_;
|
||||
literal_str_[4] = &alphas_;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void TessLangModel::RemoveInvalidCharacters(string *lm_str) {
|
||||
CharSet *char_set = cntxt_->CharacterSet();
|
||||
tesseract::string_32 lm_str32;
|
||||
CubeUtils::UTF8ToUTF32(lm_str->c_str(), &lm_str32);
|
||||
|
||||
int len = CubeUtils::StrLen(lm_str32.c_str());
|
||||
char_32 *clean_str32 = new char_32[len + 1];
|
||||
if (!clean_str32)
|
||||
return;
|
||||
int clean_len = 0;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
int class_id = char_set->ClassID((char_32)lm_str32[i]);
|
||||
if (class_id != INVALID_UNICHAR_ID) {
|
||||
clean_str32[clean_len] = lm_str32[i];
|
||||
++clean_len;
|
||||
}
|
||||
}
|
||||
clean_str32[clean_len] = 0;
|
||||
if (clean_len < len) {
|
||||
lm_str->clear();
|
||||
CubeUtils::UTF32ToUTF8(clean_str32, lm_str);
|
||||
}
|
||||
delete [] clean_str32;
|
||||
}
|
||||
|
||||
int TessLangModel::NumDawgs() const {
|
||||
return (word_dawgs_ != NULL) ?
|
||||
word_dawgs_->size() : cntxt_->TesseractObject()->getDict().NumDawgs();
|
||||
}
|
||||
|
||||
// Returns the dawgs with the given index from either the dawgs
|
||||
// stored by the Tesseract object, or the word_dawgs_.
|
||||
const Dawg *TessLangModel::GetDawg(int index) const {
|
||||
if (word_dawgs_ != NULL) {
|
||||
ASSERT_HOST(index < word_dawgs_->size());
|
||||
return (*word_dawgs_)[index];
|
||||
} else {
|
||||
ASSERT_HOST(index < cntxt_->TesseractObject()->getDict().NumDawgs());
|
||||
return cntxt_->TesseractObject()->getDict().GetDawg(index);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,247 +0,0 @@
|
|||
// ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2008 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The name of the author may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_STDINT_H_ // [
|
||||
#define _MSC_STDINT_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
|
||||
// or compiler give many errors like this:
|
||||
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
# include <wchar.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// Define _W64 macros to mark types changing their size, like intptr_t.
|
||||
#ifndef _W64
|
||||
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||
# define _W64 __w64
|
||||
# else
|
||||
# define _W64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
// 7.18.1 Integer types
|
||||
|
||||
// 7.18.1.1 Exact-width integer types
|
||||
|
||||
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||
// realize that, e.g. char has the same size as __int8
|
||||
// so we give up on __intX for them.
|
||||
#if (_MSC_VER < 1300)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
|
||||
// 7.18.1.2 Minimum-width integer types
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
|
||||
// 7.18.1.3 Fastest minimum-width integer types
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int16_t int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
typedef int64_t int_fast64_t;
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef uint16_t uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
typedef uint64_t uint_fast64_t;
|
||||
|
||||
// 7.18.1.4 Integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else // _WIN64 ][
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.1.5 Greatest-width integer types
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
|
||||
|
||||
// 7.18.2 Limits of specified-width integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
|
||||
|
||||
// 7.18.2.1 Limits of exact-width integer types
|
||||
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||
#define INT8_MAX _I8_MAX
|
||||
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||
#define INT16_MAX _I16_MAX
|
||||
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||
#define INT32_MAX _I32_MAX
|
||||
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||
#define INT64_MAX _I64_MAX
|
||||
#define UINT8_MAX _UI8_MAX
|
||||
#define UINT16_MAX _UI16_MAX
|
||||
#define UINT32_MAX _UI32_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
|
||||
// 7.18.2.2 Limits of minimum-width integer types
|
||||
#define INT_LEAST8_MIN INT8_MIN
|
||||
#define INT_LEAST8_MAX INT8_MAX
|
||||
#define INT_LEAST16_MIN INT16_MIN
|
||||
#define INT_LEAST16_MAX INT16_MAX
|
||||
#define INT_LEAST32_MIN INT32_MIN
|
||||
#define INT_LEAST32_MAX INT32_MAX
|
||||
#define INT_LEAST64_MIN INT64_MIN
|
||||
#define INT_LEAST64_MAX INT64_MAX
|
||||
#define UINT_LEAST8_MAX UINT8_MAX
|
||||
#define UINT_LEAST16_MAX UINT16_MAX
|
||||
#define UINT_LEAST32_MAX UINT32_MAX
|
||||
#define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.3 Limits of fastest minimum-width integer types
|
||||
#define INT_FAST8_MIN INT8_MIN
|
||||
#define INT_FAST8_MAX INT8_MAX
|
||||
#define INT_FAST16_MIN INT16_MIN
|
||||
#define INT_FAST16_MAX INT16_MAX
|
||||
#define INT_FAST32_MIN INT32_MIN
|
||||
#define INT_FAST32_MAX INT32_MAX
|
||||
#define INT_FAST64_MIN INT64_MIN
|
||||
#define INT_FAST64_MAX INT64_MAX
|
||||
#define UINT_FAST8_MAX UINT8_MAX
|
||||
#define UINT_FAST16_MAX UINT16_MAX
|
||||
#define UINT_FAST32_MAX UINT32_MAX
|
||||
#define UINT_FAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.4 Limits of integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
# define INTPTR_MIN INT64_MIN
|
||||
# define INTPTR_MAX INT64_MAX
|
||||
# define UINTPTR_MAX UINT64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define INTPTR_MIN INT32_MIN
|
||||
# define INTPTR_MAX INT32_MAX
|
||||
# define UINTPTR_MAX UINT32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.2.5 Limits of greatest-width integer types
|
||||
#define INTMAX_MIN INT64_MIN
|
||||
#define INTMAX_MAX INT64_MAX
|
||||
#define UINTMAX_MAX UINT64_MAX
|
||||
|
||||
// 7.18.3 Limits of other integer types
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define PTRDIFF_MIN _I64_MIN
|
||||
# define PTRDIFF_MAX _I64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define PTRDIFF_MIN _I32_MIN
|
||||
# define PTRDIFF_MAX _I32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#define SIG_ATOMIC_MIN INT_MIN
|
||||
#define SIG_ATOMIC_MAX INT_MAX
|
||||
|
||||
#ifndef SIZE_MAX // [
|
||||
# ifdef _WIN64 // [
|
||||
# define SIZE_MAX _UI64_MAX
|
||||
# else // _WIN64 ][
|
||||
# define SIZE_MAX _UI32_MAX
|
||||
# endif // _WIN64 ]
|
||||
#endif // SIZE_MAX ]
|
||||
|
||||
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
|
||||
#ifndef WCHAR_MIN // [
|
||||
# define WCHAR_MIN 0
|
||||
#endif // WCHAR_MIN ]
|
||||
#ifndef WCHAR_MAX // [
|
||||
# define WCHAR_MAX _UI16_MAX
|
||||
#endif // WCHAR_MAX ]
|
||||
|
||||
#define WINT_MIN 0
|
||||
#define WINT_MAX _UI16_MAX
|
||||
|
||||
#endif // __STDC_LIMIT_MACROS ]
|
||||
|
||||
|
||||
// 7.18.4 Limits of other integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
#define INTMAX_C INT64_C
|
||||
#define UINTMAX_C UINT64_C
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
|
||||
#endif // _MSC_STDINT_H_ ]
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,147 +0,0 @@
|
|||
// Copyright 2008 Google Inc.
|
||||
// All Rights Reserved.
|
||||
// Author: ahmadab@google.com (Ahmad Abdulkader)
|
||||
//
|
||||
// neuron.h: Declarations of a class for an object that
|
||||
// represents a single neuron in a neural network
|
||||
//
|
||||
|
||||
#ifndef NEURON_H
|
||||
#define NEURON_H
|
||||
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef USE_STD_NAMESPACE
|
||||
using std::vector;
|
||||
#endif
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
// Input Node bias values
|
||||
static const float kInputNodeBias = 0.0f;
|
||||
|
||||
class Neuron {
|
||||
public:
|
||||
// Types of nodes
|
||||
enum NeuronTypes {
|
||||
Unknown = 0,
|
||||
Input,
|
||||
Hidden,
|
||||
Output
|
||||
};
|
||||
Neuron();
|
||||
~Neuron();
|
||||
// set the forward dirty flag indicating that the
|
||||
// activation of the net is not fresh
|
||||
void Clear() {
|
||||
frwd_dirty_ = true;
|
||||
}
|
||||
// Read a binary representation of the neuron info from
|
||||
// an input buffer.
|
||||
template <class BuffType> bool ReadBinary(BuffType *input_buff) {
|
||||
float val;
|
||||
if (input_buff->Read(&val, sizeof(val)) != sizeof(val)) {
|
||||
return false;
|
||||
}
|
||||
// input nodes should have no biases
|
||||
if (node_type_ == Input) {
|
||||
bias_ = kInputNodeBias;
|
||||
} else {
|
||||
bias_ = val;
|
||||
}
|
||||
// read fanin count
|
||||
int fan_in_cnt;
|
||||
if (input_buff->Read(&fan_in_cnt, sizeof(fan_in_cnt)) !=
|
||||
sizeof(fan_in_cnt)) {
|
||||
return false;
|
||||
}
|
||||
// validate fan-in cnt
|
||||
if (fan_in_cnt != fan_in_.size()) {
|
||||
return false;
|
||||
}
|
||||
// read the weights
|
||||
for (int in = 0; in < fan_in_cnt; in++) {
|
||||
if (input_buff->Read(&val, sizeof(val)) != sizeof(val)) {
|
||||
return false;
|
||||
}
|
||||
*(fan_in_weights_[in]) = val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add a new connection from this neuron *From*
|
||||
// a target neuron using specfied params
|
||||
// Note that what is actually copied in this function are pointers to the
|
||||
// specified Neurons and weights and not the actualt values. This is by
|
||||
// design to centralize the alloction of neurons and weights and so
|
||||
// increase the locality of reference and improve cache-hits resulting
|
||||
// in a faster net. This technique resulted in a 2X-10X speedup
|
||||
// (depending on network size and processor)
|
||||
void AddFromConnection(Neuron *neuron_vec,
|
||||
float *wts_offset,
|
||||
int from_cnt);
|
||||
// Set the type of a neuron
|
||||
void set_node_type(NeuronTypes type);
|
||||
// Computes the output of the node by
|
||||
// "pulling" the output of the fan-in nodes
|
||||
void FeedForward();
|
||||
// fast computation of sigmoid function using a lookup table
|
||||
// defined in sigmoid_table.cpp
|
||||
static float Sigmoid(float activation);
|
||||
// Accessor functions
|
||||
float output() const {
|
||||
return output_;
|
||||
}
|
||||
void set_output(float out_val) {
|
||||
output_ = out_val;
|
||||
}
|
||||
int id() const {
|
||||
return id_;
|
||||
}
|
||||
int fan_in_cnt() const {
|
||||
return fan_in_.size();
|
||||
}
|
||||
Neuron * fan_in(int idx) const {
|
||||
return fan_in_[idx];
|
||||
}
|
||||
float fan_in_wts(int idx) const {
|
||||
return *(fan_in_weights_[idx]);
|
||||
}
|
||||
void set_id(int id) {
|
||||
id_ = id;
|
||||
}
|
||||
float bias() const {
|
||||
return bias_;
|
||||
}
|
||||
Neuron::NeuronTypes node_type() const {
|
||||
return node_type_;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Type of Neuron
|
||||
NeuronTypes node_type_;
|
||||
// unqique id of the neuron
|
||||
int id_;
|
||||
// node bias
|
||||
float bias_;
|
||||
// node net activation
|
||||
float activation_;
|
||||
// node output
|
||||
float output_;
|
||||
// pointers to fanin nodes
|
||||
vector<Neuron *> fan_in_;
|
||||
// pointers to fanin weights
|
||||
vector<float *> fan_in_weights_;
|
||||
// Sigmoid function lookup table used for fast computation
|
||||
// of sigmoid function
|
||||
static const float kSigmoidTable[];
|
||||
// flag determining if the activation of the node
|
||||
// is fresh or not (dirty)
|
||||
bool frwd_dirty_;
|
||||
// Initializer
|
||||
void Init();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NEURON_H__
|
|
@ -1,577 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////
|
||||
// File: pageiterator.cpp
|
||||
// Description: Iterator for tesseract page structure that avoids using
|
||||
// tesseract internal data structures.
|
||||
// Author: Ray Smith
|
||||
// Created: Fri Feb 26 14:32:09 PST 2010
|
||||
//
|
||||
// (C) Copyright 2010, 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 "pageiterator.h"
|
||||
#include "allheaders.h"
|
||||
#include "helpers.h"
|
||||
#include "pageres.h"
|
||||
#include "tesseractclass.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
PageIterator::PageIterator(PAGE_RES* page_res, Tesseract* tesseract,
|
||||
int scale, int scaled_yres,
|
||||
int rect_left, int rect_top,
|
||||
int rect_width, int rect_height)
|
||||
: page_res_(page_res), tesseract_(tesseract),
|
||||
word_(NULL), word_length_(0), blob_index_(0), cblob_it_(NULL),
|
||||
scale_(scale), scaled_yres_(scaled_yres),
|
||||
rect_left_(rect_left), rect_top_(rect_top),
|
||||
rect_width_(rect_width), rect_height_(rect_height) {
|
||||
it_ = new PAGE_RES_IT(page_res);
|
||||
PageIterator::Begin();
|
||||
}
|
||||
|
||||
PageIterator::~PageIterator() {
|
||||
delete it_;
|
||||
delete cblob_it_;
|
||||
}
|
||||
|
||||
/**
|
||||
* PageIterators may be copied! This makes it possible to iterate over
|
||||
* all the objects at a lower level, while maintaining an iterator to
|
||||
* objects at a higher level.
|
||||
*/
|
||||
PageIterator::PageIterator(const PageIterator& src)
|
||||
: page_res_(src.page_res_), tesseract_(src.tesseract_),
|
||||
word_(NULL), word_length_(src.word_length_),
|
||||
blob_index_(src.blob_index_), cblob_it_(NULL),
|
||||
scale_(src.scale_), scaled_yres_(src.scaled_yres_),
|
||||
rect_left_(src.rect_left_), rect_top_(src.rect_top_),
|
||||
rect_width_(src.rect_width_), rect_height_(src.rect_height_) {
|
||||
it_ = new PAGE_RES_IT(*src.it_);
|
||||
BeginWord(src.blob_index_);
|
||||
}
|
||||
|
||||
const PageIterator& PageIterator::operator=(const PageIterator& src) {
|
||||
page_res_ = src.page_res_;
|
||||
tesseract_ = src.tesseract_;
|
||||
scale_ = src.scale_;
|
||||
scaled_yres_ = src.scaled_yres_;
|
||||
rect_left_ = src.rect_left_;
|
||||
rect_top_ = src.rect_top_;
|
||||
rect_width_ = src.rect_width_;
|
||||
rect_height_ = src.rect_height_;
|
||||
if (it_ != NULL) delete it_;
|
||||
it_ = new PAGE_RES_IT(*src.it_);
|
||||
BeginWord(src.blob_index_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool PageIterator::PositionedAtSameWord(const PAGE_RES_IT* other) const {
|
||||
return (it_ == NULL && it_ == other) ||
|
||||
((other != NULL) && (it_ != NULL) && (*it_ == *other));
|
||||
}
|
||||
|
||||
// ============= Moving around within the page ============.
|
||||
|
||||
/** Resets the iterator to point to the start of the page. */
|
||||
void PageIterator::Begin() {
|
||||
it_->restart_page_with_empties();
|
||||
BeginWord(0);
|
||||
}
|
||||
|
||||
void PageIterator::RestartParagraph() {
|
||||
if (it_->block() == NULL) return; // At end of the document.
|
||||
PAGE_RES_IT para(page_res_);
|
||||
PAGE_RES_IT next_para(para);
|
||||
next_para.forward_paragraph();
|
||||
while (next_para.cmp(*it_) <= 0) {
|
||||
para = next_para;
|
||||
next_para.forward_paragraph();
|
||||
}
|
||||
*it_ = para;
|
||||
BeginWord(0);
|
||||
}
|
||||
|
||||
bool PageIterator::IsWithinFirstTextlineOfParagraph() const {
|
||||
PageIterator p_start(*this);
|
||||
p_start.RestartParagraph();
|
||||
return p_start.it_->row() == it_->row();
|
||||
}
|
||||
|
||||
void PageIterator::RestartRow() {
|
||||
it_->restart_row();
|
||||
BeginWord(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to the start of the next object at the given level in the
|
||||
* page hierarchy, and returns false if the end of the page was reached.
|
||||
* NOTE (CHANGED!) that ALL PageIteratorLevel level values will visit each
|
||||
* non-text block at least once.
|
||||
* Think of non text blocks as containing a single para, with at least one
|
||||
* line, with a single imaginary word, containing a single symbol.
|
||||
* The bounding boxes mark out any polygonal nature of the block, and
|
||||
* PTIsTextType(BLockType()) is false for non-text blocks.
|
||||
* Calls to Next with different levels may be freely intermixed.
|
||||
* This function iterates words in right-to-left scripts correctly, if
|
||||
* the appropriate language has been loaded into Tesseract.
|
||||
*/
|
||||
bool PageIterator::Next(PageIteratorLevel level) {
|
||||
if (it_->block() == NULL) return false; // Already at the end!
|
||||
if (it_->word() == NULL)
|
||||
level = RIL_BLOCK;
|
||||
|
||||
switch (level) {
|
||||
case RIL_BLOCK:
|
||||
it_->forward_block();
|
||||
break;
|
||||
case RIL_PARA:
|
||||
it_->forward_paragraph();
|
||||
break;
|
||||
case RIL_TEXTLINE:
|
||||
for (it_->forward_with_empties(); it_->row() == it_->prev_row();
|
||||
it_->forward_with_empties());
|
||||
break;
|
||||
case RIL_WORD:
|
||||
it_->forward_with_empties();
|
||||
break;
|
||||
case RIL_SYMBOL:
|
||||
if (cblob_it_ != NULL)
|
||||
cblob_it_->forward();
|
||||
++blob_index_;
|
||||
if (blob_index_ >= word_length_)
|
||||
it_->forward_with_empties();
|
||||
else
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
BeginWord(0);
|
||||
return it_->block() != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the iterator is at the start of an object at the given
|
||||
* level. Possible uses include determining if a call to Next(RIL_WORD)
|
||||
* moved to the start of a RIL_PARA.
|
||||
*/
|
||||
bool PageIterator::IsAtBeginningOf(PageIteratorLevel level) const {
|
||||
if (it_->block() == NULL) return false; // Already at the end!
|
||||
if (it_->word() == NULL) return true; // In an image block.
|
||||
switch (level) {
|
||||
case RIL_BLOCK:
|
||||
return blob_index_ == 0 && it_->block() != it_->prev_block();
|
||||
case RIL_PARA:
|
||||
return blob_index_ == 0 &&
|
||||
(it_->block() != it_->prev_block() ||
|
||||
it_->row()->row->para() != it_->prev_row()->row->para());
|
||||
case RIL_TEXTLINE:
|
||||
return blob_index_ == 0 && it_->row() != it_->prev_row();
|
||||
case RIL_WORD:
|
||||
return blob_index_ == 0;
|
||||
case RIL_SYMBOL:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the iterator is positioned at the last element in a
|
||||
* given level. (e.g. the last word in a line, the last line in a block)
|
||||
*/
|
||||
bool PageIterator::IsAtFinalElement(PageIteratorLevel level,
|
||||
PageIteratorLevel element) const {
|
||||
if (Empty(element)) return true; // Already at the end!
|
||||
// The result is true if we step forward by element and find we are
|
||||
// at the the end of the page or at beginning of *all* levels in:
|
||||
// [level, element).
|
||||
// When there is more than one level difference between element and level,
|
||||
// we could for instance move forward one symbol and still be at the first
|
||||
// word on a line, so we also have to be at the first symbol in a word.
|
||||
PageIterator next(*this);
|
||||
next.Next(element);
|
||||
if (next.Empty(element)) return true; // Reached the end of the page.
|
||||
while (element > level) {
|
||||
element = static_cast<PageIteratorLevel>(element - 1);
|
||||
if (!next.IsAtBeginningOf(element))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this iterator is positioned
|
||||
* before other: -1
|
||||
* equal to other: 0
|
||||
* after other: 1
|
||||
*/
|
||||
int PageIterator::Cmp(const PageIterator &other) const {
|
||||
int word_cmp = it_->cmp(*other.it_);
|
||||
if (word_cmp != 0)
|
||||
return word_cmp;
|
||||
if (blob_index_ < other.blob_index_)
|
||||
return -1;
|
||||
if (blob_index_ == other.blob_index_)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ============= Accessing data ==============.
|
||||
// Coordinate system:
|
||||
// Integer coordinates are at the cracks between the pixels.
|
||||
// The top-left corner of the top-left pixel in the image is at (0,0).
|
||||
// The bottom-right corner of the bottom-right pixel in the image is at
|
||||
// (width, height).
|
||||
// Every bounding box goes from the top-left of the top-left contained
|
||||
// pixel to the bottom-right of the bottom-right contained pixel, so
|
||||
// the bounding box of the single top-left pixel in the image is:
|
||||
// (0,0)->(1,1).
|
||||
// If an image rectangle has been set in the API, then returned coordinates
|
||||
// relate to the original (full) image, rather than the rectangle.
|
||||
|
||||
/**
|
||||
* Returns the bounding rectangle of the current object at the given level in
|
||||
* the coordinates of the working image that is pix_binary().
|
||||
* See comment on coordinate system above.
|
||||
* Returns false if there is no such object at the current position.
|
||||
*/
|
||||
bool PageIterator::BoundingBoxInternal(PageIteratorLevel level,
|
||||
int* left, int* top,
|
||||
int* right, int* bottom) const {
|
||||
if (Empty(level))
|
||||
return false;
|
||||
TBOX box;
|
||||
PARA *para = NULL;
|
||||
switch (level) {
|
||||
case RIL_BLOCK:
|
||||
box = it_->block()->block->bounding_box();
|
||||
break;
|
||||
case RIL_PARA:
|
||||
para = it_->row()->row->para();
|
||||
// explicit fall-through.
|
||||
case RIL_TEXTLINE:
|
||||
box = it_->row()->row->bounding_box();
|
||||
break;
|
||||
case RIL_WORD:
|
||||
box = it_->word()->word->bounding_box();
|
||||
break;
|
||||
case RIL_SYMBOL:
|
||||
if (cblob_it_ == NULL)
|
||||
box = it_->word()->box_word->BlobBox(blob_index_);
|
||||
else
|
||||
box = cblob_it_->data()->bounding_box();
|
||||
}
|
||||
if (level == RIL_PARA) {
|
||||
PageIterator other = *this;
|
||||
other.Begin();
|
||||
do {
|
||||
if (other.it_->block() &&
|
||||
other.it_->block()->block == it_->block()->block &&
|
||||
other.it_->row() && other.it_->row()->row &&
|
||||
other.it_->row()->row->para() == para) {
|
||||
box = box.bounding_union(other.it_->row()->row->bounding_box());
|
||||
}
|
||||
} while (other.Next(RIL_TEXTLINE));
|
||||
}
|
||||
if (level != RIL_SYMBOL || cblob_it_ != NULL)
|
||||
box.rotate(it_->block()->block->re_rotation());
|
||||
// Now we have a box in tesseract coordinates relative to the image rectangle,
|
||||
// we have to convert the coords to a top-down system.
|
||||
const int pix_height = pixGetHeight(tesseract_->pix_binary());
|
||||
const int pix_width = pixGetWidth(tesseract_->pix_binary());
|
||||
*left = ClipToRange(static_cast<int>(box.left()), 0, pix_width);
|
||||
*top = ClipToRange(pix_height - box.top(), 0, pix_height);
|
||||
*right = ClipToRange(static_cast<int>(box.right()), *left, pix_width);
|
||||
*bottom = ClipToRange(pix_height - box.bottom(), *top, pix_height);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounding rectangle of the current object at the given level in
|
||||
* coordinates of the original image.
|
||||
* See comment on coordinate system above.
|
||||
* Returns false if there is no such object at the current position.
|
||||
*/
|
||||
bool PageIterator::BoundingBox(PageIteratorLevel level,
|
||||
int* left, int* top,
|
||||
int* right, int* bottom) const {
|
||||
if (!BoundingBoxInternal(level, left, top, right, bottom))
|
||||
return false;
|
||||
// Convert to the coordinate system of the original image.
|
||||
*left = ClipToRange(*left / scale_ + rect_left_,
|
||||
rect_left_, rect_left_ + rect_width_);
|
||||
*top = ClipToRange(*top / scale_ + rect_top_,
|
||||
rect_top_, rect_top_ + rect_height_);
|
||||
*right = ClipToRange((*right + scale_ - 1) / scale_ + rect_left_,
|
||||
*left, rect_left_ + rect_width_);
|
||||
*bottom = ClipToRange((*bottom + scale_ - 1) / scale_ + rect_top_,
|
||||
*top, rect_top_ + rect_height_);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Return that there is no such object at a given level. */
|
||||
bool PageIterator::Empty(PageIteratorLevel level) const {
|
||||
if (it_->block() == NULL) return true; // Already at the end!
|
||||
if (it_->word() == NULL && level != RIL_BLOCK) return true; // image block
|
||||
if (level == RIL_SYMBOL && blob_index_ >= word_length_)
|
||||
return true; // Zero length word, or already at the end of it.
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the type of the current block. See apitypes.h for PolyBlockType. */
|
||||
PolyBlockType PageIterator::BlockType() const {
|
||||
if (it_->block() == NULL || it_->block()->block == NULL)
|
||||
return PT_UNKNOWN; // Already at the end!
|
||||
if (it_->block()->block->poly_block() == NULL)
|
||||
return PT_FLOWING_TEXT; // No layout analysis used - assume text.
|
||||
return it_->block()->block->poly_block()->isA();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a binary image of the current object at the given level.
|
||||
* The position and size match the return from BoundingBoxInternal, and so this
|
||||
* could be upscaled with respect to the original input image.
|
||||
* Use pixDestroy to delete the image after use.
|
||||
* The following methods are used to generate the images:
|
||||
* RIL_BLOCK: mask the page image with the block polygon.
|
||||
* RIL_TEXTLINE: Clip the rectangle of the line box from the page image.
|
||||
* TODO(rays) fix this to generate and use a line polygon.
|
||||
* RIL_WORD: Clip the rectangle of the word box from the page image.
|
||||
* RIL_SYMBOL: Render the symbol outline to an image for cblobs (prior
|
||||
* to recognition) or the bounding box otherwise.
|
||||
* A reconstruction of the original image (using xor to check for double
|
||||
* representation) should be reasonably accurate,
|
||||
* apart from removed noise, at the block level. Below the block level, the
|
||||
* reconstruction will be missing images and line separators.
|
||||
* At the symbol level, kerned characters will be invade the bounding box
|
||||
* if rendered after recognition, making an xor reconstruction inaccurate, but
|
||||
* an or construction better. Before recognition, symbol-level reconstruction
|
||||
* should be good, even with xor, since the images come from the connected
|
||||
* components.
|
||||
*/
|
||||
Pix* PageIterator::GetBinaryImage(PageIteratorLevel level) const {
|
||||
int left, top, right, bottom;
|
||||
if (!BoundingBoxInternal(level, &left, &top, &right, &bottom))
|
||||
return NULL;
|
||||
Pix* pix = NULL;
|
||||
switch (level) {
|
||||
case RIL_BLOCK:
|
||||
case RIL_PARA:
|
||||
int bleft, btop, bright, bbottom;
|
||||
BoundingBoxInternal(RIL_BLOCK, &bleft, &btop, &bright, &bbottom);
|
||||
pix = it_->block()->block->render_mask();
|
||||
// AND the mask and the image.
|
||||
pixRasterop(pix, 0, 0, pixGetWidth(pix), pixGetHeight(pix),
|
||||
PIX_SRC & PIX_DST, tesseract_->pix_binary(),
|
||||
bleft, btop);
|
||||
if (level == RIL_PARA) {
|
||||
// RIL_PARA needs further attention:
|
||||
// clip the paragraph from the block mask.
|
||||
Box* box = boxCreate(left - bleft, top - btop,
|
||||
right - left, bottom - top);
|
||||
Pix* pix2 = pixClipRectangle(pix, box, NULL);
|
||||
boxDestroy(&box);
|
||||
pixDestroy(&pix);
|
||||
pix = pix2;
|
||||
}
|
||||
break;
|
||||
case RIL_TEXTLINE:
|
||||
case RIL_WORD:
|
||||
case RIL_SYMBOL:
|
||||
if (level == RIL_SYMBOL && cblob_it_ != NULL &&
|
||||
cblob_it_->data()->area() != 0)
|
||||
return cblob_it_->data()->render();
|
||||
// Just clip from the bounding box.
|
||||
Box* box = boxCreate(left, top, right - left, bottom - top);
|
||||
pix = pixClipRectangle(tesseract_->pix_binary(), box, NULL);
|
||||
boxDestroy(&box);
|
||||
break;
|
||||
}
|
||||
return pix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an image of the current object at the given level in greyscale
|
||||
* if available in the input. To guarantee a binary image use BinaryImage.
|
||||
* NOTE that in order to give the best possible image, the bounds are
|
||||
* expanded slightly over the binary connected component, by the supplied
|
||||
* padding, so the top-left position of the returned image is returned
|
||||
* in (left,top). These will most likely not match the coordinates
|
||||
* returned by BoundingBox.
|
||||
* Use pixDestroy to delete the image after use.
|
||||
*/
|
||||
Pix* PageIterator::GetImage(PageIteratorLevel level, int padding,
|
||||
int* left, int* top) const {
|
||||
int right, bottom;
|
||||
if (!BoundingBox(level, left, top, &right, &bottom))
|
||||
return NULL;
|
||||
Pix* pix = tesseract_->pix_grey();
|
||||
if (pix == NULL)
|
||||
return GetBinaryImage(level);
|
||||
|
||||
// Expand the box.
|
||||
*left = MAX(*left - padding, 0);
|
||||
*top = MAX(*top - padding, 0);
|
||||
right = MIN(right + padding, rect_width_);
|
||||
bottom = MIN(bottom + padding, rect_height_);
|
||||
Box* box = boxCreate(*left, *top, right - *left, bottom - *top);
|
||||
Pix* grey_pix = pixClipRectangle(pix, box, NULL);
|
||||
boxDestroy(&box);
|
||||
if (level == RIL_BLOCK) {
|
||||
Pix* mask = it_->block()->block->render_mask();
|
||||
Pix* expanded_mask = pixCreate(right - *left, bottom - *top, 1);
|
||||
pixRasterop(expanded_mask, padding, padding,
|
||||
pixGetWidth(mask), pixGetHeight(mask),
|
||||
PIX_SRC, mask, 0, 0);
|
||||
pixDestroy(&mask);
|
||||
pixDilateBrick(expanded_mask, expanded_mask, 2*padding + 1, 2*padding + 1);
|
||||
pixInvert(expanded_mask, expanded_mask);
|
||||
pixSetMasked(grey_pix, expanded_mask, 255);
|
||||
pixDestroy(&expanded_mask);
|
||||
}
|
||||
return grey_pix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the baseline of the current object at the given level.
|
||||
* The baseline is the line that passes through (x1, y1) and (x2, y2).
|
||||
* WARNING: with vertical text, baselines may be vertical!
|
||||
*/
|
||||
bool PageIterator::Baseline(PageIteratorLevel level,
|
||||
int* x1, int* y1, int* x2, int* y2) const {
|
||||
if (it_->word() == NULL) return false; // Already at the end!
|
||||
ROW* row = it_->row()->row;
|
||||
WERD* word = it_->word()->word;
|
||||
TBOX box = (level == RIL_WORD || level == RIL_SYMBOL)
|
||||
? word->bounding_box()
|
||||
: row->bounding_box();
|
||||
int left = box.left();
|
||||
ICOORD startpt(left, static_cast<inT16>(row->base_line(left) + 0.5));
|
||||
int right = box.right();
|
||||
ICOORD endpt(right, static_cast<inT16>(row->base_line(right) + 0.5));
|
||||
// Rotate to image coordinates and convert to global image coords.
|
||||
startpt.rotate(it_->block()->block->re_rotation());
|
||||
endpt.rotate(it_->block()->block->re_rotation());
|
||||
*x1 = startpt.x() / scale_ + rect_left_;
|
||||
*y1 = (rect_height_ - startpt.y()) / scale_ + rect_top_;
|
||||
*x2 = endpt.x() / scale_ + rect_left_;
|
||||
*y2 = (rect_height_ - endpt.y()) / scale_ + rect_top_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PageIterator::Orientation(tesseract::Orientation *orientation,
|
||||
tesseract::WritingDirection *writing_direction,
|
||||
tesseract::TextlineOrder *textline_order,
|
||||
float *deskew_angle) const {
|
||||
BLOCK* block = it_->block()->block;
|
||||
|
||||
// Orientation
|
||||
FCOORD up_in_image(0.0, 1.0);
|
||||
up_in_image.unrotate(block->classify_rotation());
|
||||
up_in_image.rotate(block->re_rotation());
|
||||
|
||||
if (up_in_image.x() == 0.0F) {
|
||||
if (up_in_image.y() > 0.0F) {
|
||||
*orientation = ORIENTATION_PAGE_UP;
|
||||
} else {
|
||||
*orientation = ORIENTATION_PAGE_DOWN;
|
||||
}
|
||||
} else if (up_in_image.x() > 0.0F) {
|
||||
*orientation = ORIENTATION_PAGE_RIGHT;
|
||||
} else {
|
||||
*orientation = ORIENTATION_PAGE_LEFT;
|
||||
}
|
||||
|
||||
// Writing direction
|
||||
bool is_vertical_text = (block->classify_rotation().x() == 0.0);
|
||||
bool right_to_left = block->right_to_left();
|
||||
*writing_direction =
|
||||
is_vertical_text
|
||||
? WRITING_DIRECTION_TOP_TO_BOTTOM
|
||||
: (right_to_left
|
||||
? WRITING_DIRECTION_RIGHT_TO_LEFT
|
||||
: WRITING_DIRECTION_LEFT_TO_RIGHT);
|
||||
|
||||
// Textline Order
|
||||
bool is_mongolian = false; // TODO(eger): fix me
|
||||
*textline_order = is_vertical_text
|
||||
? (is_mongolian
|
||||
? TEXTLINE_ORDER_LEFT_TO_RIGHT
|
||||
: TEXTLINE_ORDER_RIGHT_TO_LEFT)
|
||||
: TEXTLINE_ORDER_TOP_TO_BOTTOM;
|
||||
|
||||
// Deskew angle
|
||||
FCOORD skew = block->skew(); // true horizontal for textlines
|
||||
*deskew_angle = -skew.angle();
|
||||
}
|
||||
|
||||
void PageIterator::ParagraphInfo(tesseract::ParagraphJustification *just,
|
||||
bool *is_list_item,
|
||||
bool *is_crown,
|
||||
int *first_line_indent) const {
|
||||
*just = tesseract::JUSTIFICATION_UNKNOWN;
|
||||
if (!it_->row() || !it_->row()->row || !it_->row()->row->para() ||
|
||||
!it_->row()->row->para()->model)
|
||||
return;
|
||||
|
||||
PARA *para = it_->row()->row->para();
|
||||
*is_list_item = para->is_list_item;
|
||||
*is_crown = para->is_very_first_or_continuation;
|
||||
*first_line_indent = para->model->first_indent() -
|
||||
para->model->body_indent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the internal data for iterating the blobs of a new word, then
|
||||
* moves the iterator to the given offset.
|
||||
*/
|
||||
void PageIterator::BeginWord(int offset) {
|
||||
WERD_RES* word_res = it_->word();
|
||||
if (word_res == NULL) {
|
||||
// This is a non-text block, so there is no word.
|
||||
word_length_ = 0;
|
||||
blob_index_ = 0;
|
||||
word_ = NULL;
|
||||
return;
|
||||
}
|
||||
if (word_res->best_choice != NULL) {
|
||||
// Recognition has been done, so we are using the box_word, which
|
||||
// is already baseline denormalized.
|
||||
word_length_ = word_res->best_choice->length();
|
||||
ASSERT_HOST(word_res->box_word != NULL);
|
||||
if (word_res->box_word->length() != word_length_) {
|
||||
tprintf("Corrupted word! best_choice[len=%d] = %s, box_word[len=%d]: ",
|
||||
word_length_, word_res->best_choice->unichar_string().string(),
|
||||
word_res->box_word->length());
|
||||
word_res->box_word->bounding_box().print();
|
||||
}
|
||||
ASSERT_HOST(word_res->box_word->length() == word_length_);
|
||||
word_ = NULL;
|
||||
// We will be iterating the box_word.
|
||||
if (cblob_it_ != NULL) {
|
||||
delete cblob_it_;
|
||||
cblob_it_ = NULL;
|
||||
}
|
||||
} else {
|
||||
// No recognition yet, so a "symbol" is a cblob.
|
||||
word_ = word_res->word;
|
||||
ASSERT_HOST(word_->cblob_list() != NULL);
|
||||
word_length_ = word_->cblob_list()->length();
|
||||
if (cblob_it_ == NULL) cblob_it_ = new C_BLOB_IT;
|
||||
cblob_it_->set_to_list(word_->cblob_list());
|
||||
}
|
||||
for (blob_index_ = 0; blob_index_ < offset; ++blob_index_) {
|
||||
if (cblob_it_ != NULL)
|
||||
cblob_it_->forward();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tesseract.
|
|
@ -1,5 +0,0 @@
|
|||
// 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
|
|
@ -1,43 +0,0 @@
|
|||
autotools (LINUX/UNIX...)
|
||||
======================
|
||||
|
||||
If you have checked out Tesseract from Subversion, you must generate the
|
||||
configure script.
|
||||
|
||||
If you have tesseract 3.0x installation in your system, please remove it
|
||||
before new build.
|
||||
|
||||
So, the steps for making Tesseract are:
|
||||
|
||||
$ ./autogen.sh
|
||||
$ ./configure
|
||||
$ make
|
||||
$ sudo make install
|
||||
$ sudo make install-langs
|
||||
|
||||
'sudo make install-langs' or 'sudo make install LANGS=' will install all
|
||||
available language data files in tessdata directory.
|
||||
|
||||
If you want to install just few of them than run:
|
||||
$ make install LANGS="eng ara deu"
|
||||
It will install only English, Arabic and German language datafiles (if
|
||||
they are present in tessdata directory)
|
||||
|
||||
To compile ScrollView.jar you need to download piccolo.JAVA[1] from
|
||||
http://www.piccolo2d.org/download.html and extract
|
||||
piccolo-1.2/build/piccolo.jar to tesseract/java as piccolo-1.2.jar and
|
||||
piccolo-1.2/build/piccolox.jar to tesseract/java as piccolox-1.2.jar.
|
||||
|
||||
Than run:
|
||||
$ make ScrollView.jar
|
||||
|
||||
and follow instruction on Viewer Debugging wiki[2].
|
||||
|
||||
[1] http://www.cs.umd.edu/hcil/jazz/download/piccolo/piccolo-1.2-compiled.zip
|
||||
[2] http://code.google.com/p/tesseract-ocr/wiki/ViewerDebugging
|
||||
|
||||
|
||||
WINDOWS
|
||||
=======
|
||||
|
||||
Please read vs2008/doc/index.html
|
|
@ -1,153 +0,0 @@
|
|||
/**********************************************************************
|
||||
* File: otsuthr.cpp
|
||||
* Description: Simple Otsu thresholding for binarizing images.
|
||||
* Author: Ray Smith
|
||||
* Created: Fri Mar 07 12:31:01 PST 2008
|
||||
*
|
||||
* (C) Copyright 2008, 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 <string.h>
|
||||
#include "otsuthr.h"
|
||||
|
||||
namespace tesseract {
|
||||
|
||||
// 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.
|
||||
// Delete thresholds and hi_values with delete [] after use.
|
||||
void OtsuThreshold(const unsigned char* imagedata,
|
||||
int bytes_per_pixel, int bytes_per_line,
|
||||
int left, int top, int width, int height,
|
||||
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 = 1;
|
||||
int best_hi_index = 0;
|
||||
bool any_good_hivalue = false;
|
||||
double best_hi_dist = 0.0;
|
||||
*thresholds = new int[bytes_per_pixel];
|
||||
*hi_values = new int[bytes_per_pixel];
|
||||
|
||||
for (int ch = 0; ch < bytes_per_pixel; ++ch) {
|
||||
(*thresholds)[ch] = -1;
|
||||
(*hi_values)[ch] = -1;
|
||||
// Compute the histogram of the image rectangle.
|
||||
int histogram[kHistogramSize];
|
||||
HistogramRect(imagedata + ch, bytes_per_pixel, bytes_per_line,
|
||||
left, top, width, height, histogram);
|
||||
int H;
|
||||
int best_omega_0;
|
||||
int best_t = OtsuStats(histogram, &H, &best_omega_0);
|
||||
if (best_omega_0 == 0 || best_omega_0 == H) {
|
||||
// This channel is empty.
|
||||
continue;
|
||||
}
|
||||
// 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 kHistogramSize(256) element array to count
|
||||
// occurrences of each pixel value.
|
||||
void HistogramRect(const unsigned char* imagedata,
|
||||
int bytes_per_pixel, int bytes_per_line,
|
||||
int left, int top, int width, int height,
|
||||
int* histogram) {
|
||||
int bottom = top + height;
|
||||
memset(histogram, 0, sizeof(*histogram) * kHistogramSize);
|
||||
const unsigned char* pixels = imagedata +
|
||||
top * bytes_per_line +
|
||||
left * bytes_per_pixel;
|
||||
for (int y = top; y < bottom; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
++histogram[pixels[x * bytes_per_pixel]];
|
||||
}
|
||||
pixels += 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 OtsuStats(const int* histogram, int* H_out, int* omega0_out) {
|
||||
int H = 0;
|
||||
double mu_T = 0.0;
|
||||
for (int i = 0; i < kHistogramSize; ++i) {
|
||||
H += histogram[i];
|
||||
mu_T += static_cast<double>(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 < kHistogramSize - 1; ++t) {
|
||||
omega_0 += histogram[t];
|
||||
mu_t += t * static_cast<double>(histogram[t]);
|
||||
if (omega_0 == 0)
|
||||
continue;
|
||||
omega_1 = H - omega_0;
|
||||
if (omega_1 == 0)
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace tesseract.
|
|
@ -1,91 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#ifdef __UNIX__
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#include "tprintf.h"
|
||||
#include "errcode.h"
|
||||
|
||||
const ERRCODE BADERRACTION = "Illegal error action";
|
||||
#define MAX_MSG 1024
|
||||
|
||||
/**********************************************************************
|
||||
* 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
|
||||
TessErrorLogCode 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 _WIN32
|
||||
//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");
|
||||
|
||||
fprintf(stderr, msg);
|
||||
|
||||
int* p = NULL;
|
||||
switch (action) {
|
||||
case DBG:
|
||||
case TESSLOG:
|
||||
return; //report only
|
||||
case TESSEXIT:
|
||||
//err_exit();
|
||||
case ABORT:
|
||||
// Create a deliberate segv as the stack trace is more useful that way.
|
||||
if (!*p)
|
||||
abort();
|
||||
default:
|
||||
BADERRACTION.error ("error", ABORT, NULL);
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче